├── AndroidDemos ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── zhangtielei │ │ │ └── demos │ │ │ └── async │ │ │ └── programming │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── zhangtielei │ │ │ └── demos │ │ │ └── async │ │ │ └── programming │ │ │ ├── MainActivity.java │ │ │ ├── callback │ │ │ ├── download │ │ │ │ ├── v1 │ │ │ │ │ ├── DownloadListener.java │ │ │ │ │ └── Downloader.java │ │ │ │ ├── v2 │ │ │ │ │ ├── DownloadListener.java │ │ │ │ │ ├── Downloader.java │ │ │ │ │ └── MyDownloader.java │ │ │ │ ├── v3 │ │ │ │ │ ├── DownloadListener.java │ │ │ │ │ ├── Downloader.java │ │ │ │ │ └── MyDownloader.java │ │ │ │ └── v3_simplified │ │ │ │ │ ├── CallbackHellExample.java │ │ │ │ │ ├── DownloadListener.java │ │ │ │ │ ├── Downloader.java │ │ │ │ │ └── MyDownloader.java │ │ │ └── emoji │ │ │ │ ├── EmojiDownloadContext.java │ │ │ │ ├── EmojiDownloadDemoListActivity.java │ │ │ │ ├── EmojiDownloader.java │ │ │ │ ├── EmojiPackage.java │ │ │ │ ├── v1 │ │ │ │ ├── EmojiDownloadDemoActivity.java │ │ │ │ └── MyEmojiDownloader.java │ │ │ │ ├── v2 │ │ │ │ ├── EmojiDownloadDemoActivity.java │ │ │ │ └── MyEmojiDownloader.java │ │ │ │ ├── v3 │ │ │ │ ├── EmojiDownloadDemoActivity.java │ │ │ │ └── MyEmojiDownloader.java │ │ │ │ └── v4 │ │ │ │ ├── EmojiDownloadDemoActivity.java │ │ │ │ └── MyEmojiDownloader.java │ │ │ ├── common │ │ │ ├── AsyncCallback.java │ │ │ └── utils │ │ │ │ └── TextLogUtil.java │ │ │ ├── introduction │ │ │ └── servicebinding │ │ │ │ ├── ServiceBindingDemoActivity.java │ │ │ │ └── service │ │ │ │ └── SomeService.java │ │ │ ├── multitask │ │ │ ├── MultiTaskDemoListActivity.java │ │ │ ├── http │ │ │ │ ├── HttpListener.java │ │ │ │ ├── HttpResult.java │ │ │ │ ├── HttpService.java │ │ │ │ └── mock │ │ │ │ │ └── MockHttpService.java │ │ │ ├── multilevelcaching │ │ │ │ ├── ImageLoader.java │ │ │ │ ├── ImageLoaderDemoActivity.java │ │ │ │ ├── ImageLoaderListener.java │ │ │ │ ├── diskcache │ │ │ │ │ ├── ImageDiskCache.java │ │ │ │ │ └── mock │ │ │ │ │ │ └── MockImageDiskCache.java │ │ │ │ ├── memcache │ │ │ │ │ ├── ImageMemCache.java │ │ │ │ │ └── mock │ │ │ │ │ │ └── MockImageMemCache.java │ │ │ │ └── mock │ │ │ │ │ └── MyDemoImageLoader.java │ │ │ ├── pagecaching │ │ │ │ ├── PageCachingDemoActivity.java │ │ │ │ ├── localcache │ │ │ │ │ ├── LocalDataCache.java │ │ │ │ │ └── mock │ │ │ │ │ │ └── MockLocalDataCache.java │ │ │ │ └── model │ │ │ │ │ ├── HttpRequest.java │ │ │ │ │ └── HttpResponse.java │ │ │ └── simultaneousrequests │ │ │ │ ├── MultiRequestsDemoActivity.java │ │ │ │ ├── model │ │ │ │ ├── HttpRequest1.java │ │ │ │ ├── HttpRequest2.java │ │ │ │ ├── HttpResponse1.java │ │ │ │ └── HttpResponse2.java │ │ │ │ └── rxjavasolution │ │ │ │ └── MultiRequestsDemoActivity.java │ │ │ └── queueing │ │ │ ├── QueueingDemoListActivity.java │ │ │ ├── v1 │ │ │ ├── TSQBasedTaskQueue.java │ │ │ ├── Task.java │ │ │ ├── TaskQueue.java │ │ │ └── TaskQueueDemoActivity.java │ │ │ ├── v2 │ │ │ ├── CallbackBasedTaskQueue.java │ │ │ ├── Task.java │ │ │ ├── TaskQueue.java │ │ │ ├── TaskQueueDemoActivity.java │ │ │ └── mock │ │ │ │ └── MockAsyncTask.java │ │ │ └── v3 │ │ │ ├── RxJavaBasedTaskQueue.java │ │ │ ├── Task.java │ │ │ ├── TaskQueue.java │ │ │ ├── TaskQueueDemoActivity.java │ │ │ └── mock │ │ │ └── MockAsyncTask.java │ │ └── res │ │ ├── layout │ │ ├── activity_item_list.xml │ │ └── activity_log_display.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── LICENSE ├── README.md └── iOSDemos ├── iOSDemos.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── iOSDemos ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── ReachabiityTestViewController.h ├── ReachabiityTestViewController.m ├── Reachability.h ├── Reachability.m ├── ServerConnection.h ├── ServerConnection.m ├── ViewController.h ├── ViewController.m └── main.m /AndroidDemos/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/* 4 | *.iml 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /AndroidDemos/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /AndroidDemos/app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:2.0.0' 7 | } 8 | } 9 | apply plugin: 'com.android.application' 10 | 11 | repositories { 12 | jcenter() 13 | } 14 | 15 | android { 16 | compileSdkVersion 23 17 | buildToolsVersion "23.0.3" 18 | 19 | defaultConfig { 20 | applicationId "com.zhangtielei.demos.async.programming" 21 | minSdkVersion 9 22 | targetSdkVersion 23 23 | versionCode 1 24 | versionName "1.0" 25 | } 26 | 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_6 29 | targetCompatibility JavaVersion.VERSION_1_6 30 | } 31 | buildTypes { 32 | release { 33 | minifyEnabled false 34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 35 | } 36 | } 37 | } 38 | 39 | dependencies { 40 | compile fileTree(dir: 'libs', include: ['*.jar']) 41 | compile 'com.android.support:appcompat-v7:23.2.1' 42 | compile 'io.reactivex:rxandroid:1.2.0' 43 | compile 'io.reactivex:rxjava:1.1.9' 44 | } 45 | -------------------------------------------------------------------------------- /AndroidDemos/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/charleszhang/Documents/sdk/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/androidTest/java/com/zhangtielei/demos/async/programming/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.zhangtielei.demos.async.programming; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 26 | 27 | 30 | 31 | 34 | 35 | 36 | 40 | 41 | 42 | 45 | 46 | 49 | 50 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 69 | 70 | 73 | 74 | 77 | 78 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming; 17 | 18 | import android.content.Intent; 19 | import android.content.res.Resources; 20 | import android.os.Bundle; 21 | import android.support.v7.app.AppCompatActivity; 22 | import android.view.View; 23 | import android.widget.AdapterView; 24 | import android.widget.ArrayAdapter; 25 | import android.widget.ListAdapter; 26 | import android.widget.ListView; 27 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloadDemoListActivity; 28 | import com.zhangtielei.demos.async.programming.introduction.servicebinding.ServiceBindingDemoActivity; 29 | import com.zhangtielei.demos.async.programming.multitask.MultiTaskDemoListActivity; 30 | import com.zhangtielei.demos.async.programming.queueing.QueueingDemoListActivity; 31 | 32 | /** 33 | * 演示程序的入口页面. 34 | */ 35 | public class MainActivity extends AppCompatActivity { 36 | 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_item_list); 42 | 43 | ListView listView = (ListView) findViewById(R.id.item_list); 44 | Resources resources = getResources(); 45 | String[] textList = new String[] { 46 | resources.getString(R.string.blog_title_1), 47 | resources.getString(R.string.blog_title_2), 48 | resources.getString(R.string.blog_title_3), 49 | resources.getString(R.string.blog_title_4), 50 | resources.getString(R.string.blog_title_5), 51 | resources.getString(R.string.blog_title_6), 52 | resources.getString(R.string.blog_title_7), 53 | }; 54 | ListAdapter listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, textList); 55 | listView.setAdapter(listAdapter); 56 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 57 | @Override 58 | public void onItemClick(AdapterView parent, View view, int position, long id) { 59 | switch (position) { 60 | case 0: 61 | { 62 | Intent intent = new Intent(MainActivity.this, ServiceBindingDemoActivity.class); 63 | startActivity(intent); 64 | break; 65 | } 66 | case 1: 67 | { 68 | Intent intent = new Intent(MainActivity.this, EmojiDownloadDemoListActivity.class); 69 | startActivity(intent); 70 | break; 71 | } 72 | case 2: 73 | { 74 | Intent intent = new Intent(MainActivity.this, MultiTaskDemoListActivity.class); 75 | startActivity(intent); 76 | break; 77 | } 78 | case 3: 79 | { 80 | Intent intent = new Intent(MainActivity.this, QueueingDemoListActivity.class); 81 | startActivity(intent); 82 | break; 83 | } 84 | case 4: 85 | break; 86 | case 5: 87 | break; 88 | case 6: 89 | break; 90 | default: 91 | break; 92 | } 93 | } 94 | }); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v1/DownloadListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v1; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/2. 20 | * 下载器回调接口定义(没有详细的错误码定义). 21 | */ 22 | public interface DownloadListener { 23 | /** 24 | * 下载结束回调. 25 | * @param result 下载结果. true表示下载成功, false表示下载失败. 26 | * @param url 资源地址 27 | * @param localPath 下载后的资源存储位置. 只有result=true时才有效. 28 | */ 29 | void downloadFinished(boolean result, String url, String localPath); 30 | 31 | /** 32 | * 下载进度回调. 33 | * @param url 资源地址 34 | * @param downloadedSize 已下载大小. 35 | * @param totalSize 资源总大小. 36 | */ 37 | void downloadProgress(String url, long downloadedSize, long totalSize); 38 | } -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v1/Downloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v1; 17 | 18 | /** 19 | * Created by Tielei Zhang on 4/17/16. 20 | * 下载器接口定义. 21 | */ 22 | public interface Downloader { 23 | /** 24 | * 设置回调监听器. 25 | * @param listener 26 | */ 27 | void setListener(DownloadListener listener); 28 | /** 29 | * 启动资源的下载. 30 | * @param url 要下载的资源地址. 31 | * @param localPath 资源下载后要存储的本地位置. 32 | */ 33 | void startDownload(String url, String localPath); 34 | } 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v2/DownloadListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v2; 17 | 18 | /** 19 | * Created by Tielei Zhang on 4/17/16. 20 | * 下载器回调接口定义(带有详细的错误码定义). 21 | */ 22 | public interface DownloadListener { 23 | /** 24 | * 错误码定义 25 | */ 26 | public static final int SUCCESS = 0;//成功 27 | public static final int INVALID_PARAMS = 1;//输入参数有误 28 | public static final int NETWORK_UNAVAILABLE = 2;//网络不可用 29 | public static final int UNKNOWN_HOST = 3;//域名解析失败 30 | public static final int CONNECT_TIMEOUT = 4;//连接超时 31 | public static final int HTTP_STATUS_NOT_OK = 5;//下载请求返回非200 32 | public static final int SDCARD_NOT_EXISTS = 6;//SD卡不存在(下载的资源没地方存) 33 | public static final int SD_CARD_NO_SPACE_LEFT = 7;//SD卡空间不足(下载的资源没地方存) 34 | public static final int READ_ONLY_FILE_SYSTEM = 8;//文件系统只读(下载的资源没地方存) 35 | public static final int LOCAL_IO_ERROR = 9;//本地SD存取有关的错误 36 | public static final int UNKNOWN_FAILED = 10;//其它未知错误 37 | 38 | /** 39 | * 下载成功回调. 40 | * @param url 资源地址 41 | * @param localPath 下载后的资源存储位置. 42 | */ 43 | void downloadSuccess(String url, String localPath); 44 | /** 45 | * 下载失败回调. 46 | * @param url 资源地址 47 | * @param errorCode 错误码. 48 | * @param errorMessage 错误信息简短描述. 供调用者理解错误原因. 49 | */ 50 | void downloadFailed(String url, int errorCode, String errorMessage); 51 | 52 | /** 53 | * 下载进度回调. 54 | * @param url 资源地址 55 | * @param downloadedSize 已下载大小. 56 | * @param totalSize 资源总大小. 57 | */ 58 | void downloadProgress(String url, long downloadedSize, long totalSize); 59 | } 60 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v2/Downloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v2; 17 | 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/15. 21 | */ 22 | public interface Downloader { 23 | /** 24 | * 设置回调监听器. 25 | * @param listener 26 | */ 27 | void setListener(DownloadListener listener); 28 | /** 29 | * 启动资源的下载. 30 | * @param url 要下载的资源地址. 31 | * @param localPath 资源下载后要存储的本地位置. 32 | */ 33 | void startDownload(String url, String localPath); 34 | } 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v2/MyDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v2; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | 21 | import java.util.Random; 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * Created by Tielei Zhang on 16/5/1. 28 | * 下载器的实现(stub). 29 | */ 30 | public class MyDownloader implements Downloader { 31 | private DownloadListener listener; 32 | 33 | private ExecutorService executorService = Executors.newCachedThreadPool(); 34 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 35 | private Random rand = new Random(); 36 | 37 | @Override 38 | public void setListener(DownloadListener listener) { 39 | this.listener = listener; 40 | } 41 | 42 | @Override 43 | public void startDownload(final String url, final String localPath) { 44 | executorService.execute(new Runnable() { 45 | @Override 46 | public void run() { 47 | //模拟请求执行, 随机执行0~3秒 48 | try { 49 | TimeUnit.MILLISECONDS.sleep(rand.nextInt(3000)); 50 | } catch (InterruptedException e) { 51 | e.printStackTrace(); 52 | } 53 | 54 | //模拟一个下载成功回调 55 | mainHandler.post(new Runnable() { 56 | @Override 57 | public void run() { 58 | try { 59 | if (listener != null) { 60 | listener.downloadSuccess(url, localPath); 61 | } 62 | } 63 | catch (Throwable e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | }); 68 | } 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3/DownloadListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/2. 20 | * 下载器回调接口定义(支持回传上下文参数). 21 | */ 22 | public interface DownloadListener { 23 | /** 24 | * 错误码定义 25 | */ 26 | public static final int SUCCESS = 0;//成功 27 | public static final int INVALID_PARAMS = 1;//输入参数有误 28 | public static final int NETWORK_UNAVAILABLE = 2;//网络不可用 29 | public static final int UNKNOWN_HOST = 3;//域名解析失败 30 | public static final int CONNECT_TIMEOUT = 4;//连接超时 31 | public static final int HTTP_STATUS_NOT_OK = 5;//下载请求返回非200 32 | public static final int SDCARD_NOT_EXISTS = 6;//SD卡不存在(下载的资源没地方存) 33 | public static final int SD_CARD_NO_SPACE_LEFT = 7;//SD卡空间不足(下载的资源没地方存) 34 | public static final int READ_ONLY_FILE_SYSTEM = 8;//文件系统只读(下载的资源没地方存) 35 | public static final int LOCAL_IO_ERROR = 9;//本地SD存取有关的错误 36 | public static final int UNKNOWN_FAILED = 10;//其它未知错误 37 | 38 | /** 39 | * 下载成功回调. 40 | * @param url 资源地址 41 | * @param localPath 下载后的资源存储位置. 42 | * @param contextData 上下文数据. 43 | */ 44 | void downloadSuccess(String url, String localPath, Object contextData); 45 | /** 46 | * 下载失败回调. 47 | * @param url 资源地址 48 | * @param errorCode 错误码. 49 | * @param errorMessage 错误信息简短描述. 供调用者理解错误原因. 50 | * @param contextData 上下文数据. 51 | */ 52 | void downloadFailed(String url, int errorCode, String errorMessage, Object contextData); 53 | 54 | /** 55 | * 下载进度回调. 56 | * @param url 资源地址 57 | * @param downloadedSize 已下载大小. 58 | * @param totalSize 资源总大小. 59 | * @param contextData 上下文数据. 60 | */ 61 | void downloadProgress(String url, long downloadedSize, long totalSize, Object contextData); 62 | } 63 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3/Downloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/2. 20 | * 下载器接口定义(可以传递上下文参数). 21 | */ 22 | public interface Downloader { 23 | /** 24 | * 设置回调监听器. 25 | * @param listener 26 | */ 27 | void setListener(DownloadListener listener); 28 | /** 29 | * 启动资源的下载. 30 | * @param url 要下载的资源地址. 31 | * @param localPath 资源下载后要存储的本地位置. 32 | * @param contextData 上下文数据, 在回调接口中会透传回去.可以是任何类型. 33 | */ 34 | void startDownload(String url, String localPath, Object contextData); 35 | } 36 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3/MyDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | 21 | import java.util.Random; 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * Created by Tielei Zhang on 16/5/2. 28 | * 下载器的实现(stub). 接口支持上下文参数. 29 | */ 30 | public class MyDownloader implements Downloader { 31 | private DownloadListener listener; 32 | 33 | private ExecutorService executorService = Executors.newCachedThreadPool(); 34 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 35 | private Random rand = new Random(); 36 | 37 | @Override 38 | public void setListener(DownloadListener listener) { 39 | this.listener = listener; 40 | } 41 | 42 | @Override 43 | public void startDownload(final String url, final String localPath, final Object contextData) { 44 | executorService.execute(new Runnable() { 45 | @Override 46 | public void run() { 47 | //模拟请求执行, 随机执行0~3秒 48 | try { 49 | TimeUnit.MILLISECONDS.sleep(rand.nextInt(3000)); 50 | } catch (InterruptedException e) { 51 | e.printStackTrace(); 52 | } 53 | 54 | //模拟一个下载成功回调 55 | mainHandler.post(new Runnable() { 56 | @Override 57 | public void run() { 58 | try { 59 | if (listener != null) { 60 | listener.downloadSuccess(url, localPath, contextData); 61 | } 62 | } 63 | catch (Throwable e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | }); 68 | } 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3_simplified/CallbackHellExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3_simplified; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/11. 20 | * 演示多层嵌套导致的callback hell 21 | */ 22 | public class CallbackHellExample { 23 | /** 24 | * 连续下载3个文件 25 | * @param url1 26 | * @param url2 27 | * @param url3 28 | */ 29 | public void downloadThreeFiles(final String url1, final String url2, final String url3) { 30 | final Downloader downloader = new MyDownloader(); 31 | downloader.startDownload(url1, localPathForUrl(url1), null, new DownloadListener() { 32 | @Override 33 | public void downloadFinished(int errorCode, String url, String localPath, Object contextData) { 34 | if (errorCode != DownloadListener.SUCCESS) { 35 | //...错误处理 36 | } 37 | else { 38 | //下载第二个URL 39 | downloader.startDownload(url2, localPathForUrl(url2), null, new DownloadListener() { 40 | @Override 41 | public void downloadFinished(int errorCode, String url, String localPath, Object contextData) { 42 | if (errorCode != DownloadListener.SUCCESS) { 43 | //...错误处理 44 | } 45 | else { 46 | //下载第三个URL 47 | downloader.startDownload(url3, localPathForUrl(url3), null, new DownloadListener( 48 | 49 | ) { 50 | @Override 51 | public void downloadFinished(int errorCode, String url, String localPath, Object contextData) { 52 | //...最终结果处理 53 | } 54 | }); 55 | } 56 | } 57 | }); 58 | } 59 | } 60 | }); 61 | } 62 | 63 | private String localPathForUrl(String url) { 64 | //TODO: 65 | return null; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3_simplified/DownloadListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3_simplified; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/11. 20 | * 下载器回调接口定义(支持回传上下文参数). 21 | */ 22 | public interface DownloadListener { 23 | /** 24 | * 错误码定义 25 | */ 26 | public static final int SUCCESS = 0;//成功 27 | public static final int INVALID_PARAMS = 1;//输入参数有误 28 | public static final int NETWORK_UNAVAILABLE = 2;//网络不可用 29 | public static final int UNKNOWN_HOST = 3;//域名解析失败 30 | public static final int CONNECT_TIMEOUT = 4;//连接超时 31 | public static final int HTTP_STATUS_NOT_OK = 5;//下载请求返回非200 32 | public static final int SDCARD_NOT_EXISTS = 6;//SD卡不存在(下载的资源没地方存) 33 | public static final int SD_CARD_NO_SPACE_LEFT = 7;//SD卡空间不足(下载的资源没地方存) 34 | public static final int READ_ONLY_FILE_SYSTEM = 8;//文件系统只读(下载的资源没地方存) 35 | public static final int LOCAL_IO_ERROR = 9;//本地SD存取有关的错误 36 | public static final int UNKNOWN_FAILED = 10;//其它未知错误 37 | 38 | /** 39 | * 下载结束回调. 40 | * @param errorCode 错误码. SUCCESS表示下载成功, 其它错误码表示下载失败. 41 | * @param url 资源地址. 42 | * @param localPath 下载后的资源存储位置. 43 | * @param contextData 上下文数据. 44 | */ 45 | void downloadFinished(int errorCode, String url, String localPath, Object contextData); 46 | } 47 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3_simplified/Downloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3_simplified; 17 | 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/11. 21 | * 下载器接口定义(可以传递上下文参数). 22 | */ 23 | public interface Downloader { 24 | /** 25 | * 启动资源的下载. 26 | * @param url 要下载的资源地址. 27 | * @param localPath 资源下载后要存储的本地位置. 28 | * @param contextData 上下文数据, 在回调接口中会透传回去.可以是任何类型. 29 | * @param listener 回调接口实例. 30 | */ 31 | void startDownload(String url, String localPath, Object contextData, DownloadListener listener); 32 | } 33 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/download/v3_simplified/MyDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.download.v3_simplified; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/11. 20 | * 下载器的实现(stub). 21 | */ 22 | public class MyDownloader implements Downloader { 23 | @Override 24 | public void startDownload(String url, String localPath, Object contextData, DownloadListener listener) { 25 | //TODO: 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/EmojiDownloadContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by Tielei Zhang on 16/5/1. 23 | * 表情包下载上下文 24 | */ 25 | public class EmojiDownloadContext { 26 | /** 27 | * 当前在下载的表情包 28 | */ 29 | public EmojiPackage emojiPackage; 30 | /** 31 | * 已经下载完的表情图片计数 32 | */ 33 | public int downloadedEmoji; 34 | /** 35 | * 下载到表情包本地地址 36 | */ 37 | public List localPathList = new ArrayList(); 38 | } 39 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/EmojiDownloadDemoListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.callback.emoji; 18 | 19 | import android.content.Intent; 20 | import android.content.res.Resources; 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.view.View; 24 | import android.widget.AdapterView; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ListAdapter; 27 | import android.widget.ListView; 28 | import com.zhangtielei.demos.async.programming.R; 29 | import com.zhangtielei.demos.async.programming.callback.emoji.v1.EmojiDownloadDemoActivity; 30 | 31 | /** 32 | * 表情包下载演示程序入口列表 33 | */ 34 | public class EmojiDownloadDemoListActivity extends AppCompatActivity { 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_item_list); 40 | 41 | ListView listView = (ListView) findViewById(R.id.item_list); 42 | Resources resources = getResources(); 43 | String[] textList = new String[] { 44 | resources.getString(R.string.emoji_download_demo_1), 45 | resources.getString(R.string.emoji_download_demo_2), 46 | resources.getString(R.string.emoji_download_demo_3), 47 | resources.getString(R.string.emoji_download_demo_4), 48 | }; 49 | ListAdapter listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, textList); 50 | listView.setAdapter(listAdapter); 51 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 52 | @Override 53 | public void onItemClick(AdapterView parent, View view, int position, long id) { 54 | switch (position) { 55 | case 0: 56 | { 57 | Intent intent = new Intent(EmojiDownloadDemoListActivity.this, EmojiDownloadDemoActivity.class); 58 | startActivity(intent); 59 | break; 60 | } 61 | case 1: 62 | { 63 | Intent intent = new Intent(EmojiDownloadDemoListActivity.this, com.zhangtielei.demos.async.programming.callback.emoji.v2.EmojiDownloadDemoActivity.class); 64 | startActivity(intent); 65 | break; 66 | } 67 | case 2: 68 | { 69 | Intent intent = new Intent(EmojiDownloadDemoListActivity.this, com.zhangtielei.demos.async.programming.callback.emoji.v3.EmojiDownloadDemoActivity.class); 70 | startActivity(intent); 71 | break; 72 | } 73 | case 3: 74 | { 75 | Intent intent = new Intent(EmojiDownloadDemoListActivity.this, com.zhangtielei.demos.async.programming.callback.emoji.v4.EmojiDownloadDemoActivity.class); 76 | startActivity(intent); 77 | break; 78 | } 79 | default: 80 | break; 81 | } 82 | } 83 | }); 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/EmojiDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/1. 20 | * 表情包下载器接口. 21 | */ 22 | public interface EmojiDownloader { 23 | /** 24 | * 开始下载指定的表情包 25 | * @param emojiPackage 26 | */ 27 | void startDownloadEmoji(EmojiPackage emojiPackage); 28 | 29 | /** 30 | * 设置监听器. 31 | * @param listener 32 | */ 33 | void setEmojiDownloadListener(EmojiDownloadListener listener); 34 | 35 | /** 36 | * 这里定义回调相关的接口. 不是我们要讨论的重点. 37 | * 这里的定义用于完整演示. 38 | */ 39 | 40 | interface EmojiDownloadListener { 41 | /** 42 | * 表情包下载成功回调. 43 | * @param emojiPackage 44 | */ 45 | void emojiDownloadSuccess(EmojiPackage emojiPackage); 46 | 47 | /** 48 | * 表情包下载失败回调. 49 | * @param emojiPackage 50 | * @param errorCode 为简单起见, 这里的值复用{@link com.zhangtielei.demos.async.programming.callback.download.v3.DownloadListener}中的错误码定义. 51 | * @param errorMessage 52 | */ 53 | void emojiDownloadFailed(EmojiPackage emojiPackage, int errorCode, String errorMessage); 54 | 55 | /** 56 | * 表情包下载进度回调. 每次下载完一个图片文件后回调一次. 57 | * @param emojiPackage 58 | * @param downloadEmojiUrl 当前刚下载完的图片文件URL 59 | */ 60 | void emojiDownloadProgress(EmojiPackage emojiPackage, String downloadEmojiUrl); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/EmojiPackage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * Created by Tielei Zhang on 16/5/1. 22 | * 表情包数据结构. 23 | * 24 | */ 25 | public class EmojiPackage { 26 | /** 27 | * 表情包ID 28 | */ 29 | public long emojiId; 30 | /** 31 | * 表情包图片列表 32 | */ 33 | public List emojiUrls; 34 | } 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v1/EmojiDownloadDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.callback.emoji.v1; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 24 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 25 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 26 | 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * 通过全局保存一份上下文来实现表情包下载的演示页面. 31 | */ 32 | public class EmojiDownloadDemoActivity extends AppCompatActivity { 33 | private TextView description; 34 | private TextView logTextView; 35 | 36 | private EmojiDownloader emojiDownloader; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_log_display); 42 | 43 | description = (TextView) findViewById(R.id.description); 44 | logTextView = (TextView) findViewById(R.id.log_display); 45 | 46 | description.setText(R.string.emoji_download_demo1_description); 47 | 48 | emojiDownloader = new MyEmojiDownloader(); 49 | emojiDownloader.setEmojiDownloadListener(new EmojiDownloader.EmojiDownloadListener() { 50 | @Override 51 | public void emojiDownloadSuccess(EmojiPackage emojiPackage) { 52 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded success!"); 53 | } 54 | 55 | @Override 56 | public void emojiDownloadFailed(EmojiPackage emojiPackage, int errorCode, String errorMessage) { 57 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded failed! errorCode: " + errorCode + ", errorMessage: " + errorMessage); 58 | } 59 | 60 | @Override 61 | public void emojiDownloadProgress(EmojiPackage emojiPackage, String downloadEmojiUrl) { 62 | TextLogUtil.println(logTextView, "progress: " + downloadEmojiUrl + " downloaded..."); 63 | } 64 | }); 65 | 66 | //构造要下载的表情包 67 | EmojiPackage emojiPackage = new EmojiPackage(); 68 | emojiPackage.emojiId = 1001; 69 | emojiPackage.emojiUrls = new ArrayList(); 70 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/1.png"); 71 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/2.png"); 72 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/3.png"); 73 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/4.png"); 74 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/5.png"); 75 | 76 | TextLogUtil.println(logTextView, "Start downloading emoji package (emojiId: " + emojiPackage.emojiId + ") with " + emojiPackage.emojiUrls.size() + " URLs"); 77 | emojiDownloader.startDownloadEmoji(emojiPackage); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v1/MyEmojiDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji.v1; 17 | 18 | import com.zhangtielei.demos.async.programming.callback.download.v2.DownloadListener; 19 | import com.zhangtielei.demos.async.programming.callback.download.v2.Downloader; 20 | import com.zhangtielei.demos.async.programming.callback.download.v2.MyDownloader; 21 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloadContext; 22 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 24 | 25 | import java.util.List; 26 | 27 | /** 28 | * Created by Tielei Zhang on 16/5/1. 29 | * EmojiDownloader的第1个版本的实现: 使用全局一份上下文结构. 30 | */ 31 | public class MyEmojiDownloader implements EmojiDownloader, DownloadListener { 32 | /** 33 | * 全局保存一份的表情包下载上下文. 34 | */ 35 | private EmojiDownloadContext downloadContext; 36 | 37 | private Downloader downloader; 38 | private EmojiDownloadListener listener; 39 | 40 | public MyEmojiDownloader() { 41 | //实例化有一个下载器. 42 | downloader = new MyDownloader(); 43 | downloader.setListener(this); 44 | } 45 | 46 | @Override 47 | public void startDownloadEmoji(EmojiPackage emojiPackage) { 48 | if (downloadContext == null) { 49 | //创建下载上下文数据 50 | downloadContext = new EmojiDownloadContext(); 51 | downloadContext.emojiPackage = emojiPackage; 52 | //启动第0个表情图片文件的下载 53 | downloader.startDownload(emojiPackage.emojiUrls.get(0), 54 | getLocalPathForEmoji(emojiPackage, 0)); 55 | } 56 | } 57 | 58 | @Override 59 | public void setEmojiDownloadListener(EmojiDownloadListener listener) { 60 | this.listener = listener; 61 | } 62 | 63 | @Override 64 | public void downloadSuccess(String url, String localPath) { 65 | downloadContext.localPathList.add(localPath); 66 | downloadContext.downloadedEmoji++; 67 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 68 | if (downloadContext.downloadedEmoji < emojiPackage.emojiUrls.size()) { 69 | //还没下载完, 产生一个进度回调 70 | try { 71 | if (listener != null) { 72 | listener.emojiDownloadProgress(emojiPackage, url); 73 | } 74 | } 75 | catch (Throwable e) { 76 | e.printStackTrace(); 77 | } 78 | //继续下载下一个表情图片 79 | String nextUrl = emojiPackage.emojiUrls.get(downloadContext.downloadedEmoji); 80 | downloader.startDownload(nextUrl, 81 | getLocalPathForEmoji(emojiPackage, downloadContext.downloadedEmoji)); 82 | } 83 | else { 84 | //已经下载完 85 | installEmojiPackageLocally(emojiPackage, downloadContext.localPathList); 86 | 87 | downloadContext = null;//这个状态清理操作应该在回调之前 88 | 89 | //成功回调 90 | try { 91 | if (listener != null) { 92 | listener.emojiDownloadProgress(emojiPackage, url);//最后一次进度回调. 93 | listener.emojiDownloadSuccess(emojiPackage); 94 | } 95 | } 96 | catch (Throwable e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | } 101 | 102 | @Override 103 | public void downloadFailed(String url, int errorCode, String errorMessage) { 104 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 105 | 106 | downloadContext = null;//这个状态清理操作应该在回调之前 107 | 108 | //失败回调 109 | try { 110 | if (listener != null) { 111 | listener.emojiDownloadFailed(emojiPackage, errorCode, errorMessage); 112 | } 113 | } 114 | catch (Throwable e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | 119 | @Override 120 | public void downloadProgress(String url, long downloadedSize, long totalSize) { 121 | //TODO: 122 | } 123 | 124 | /** 125 | * 计算表情包中第i个表情图片文件的下载地址. 126 | * @param emojiPackage 127 | * @param i 128 | * @return 129 | */ 130 | private String getLocalPathForEmoji(EmojiPackage emojiPackage, int i) { 131 | //TODO: 132 | return null; 133 | } 134 | 135 | /** 136 | * 把表情包安装到本地 137 | * @param emojiPackage 138 | * @param localPathList 139 | */ 140 | private void installEmojiPackageLocally(EmojiPackage emojiPackage, List localPathList) { 141 | //TODO: 142 | return; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v2/EmojiDownloadDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.callback.emoji.v2; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 24 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 25 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 26 | 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * 通过用映射关系来保存上下文来实现表情包下载的演示页面. 31 | */ 32 | public class EmojiDownloadDemoActivity extends AppCompatActivity { 33 | private TextView description; 34 | private TextView logTextView; 35 | 36 | private EmojiDownloader emojiDownloader; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_log_display); 42 | 43 | description = (TextView) findViewById(R.id.description); 44 | logTextView = (TextView) findViewById(R.id.log_display); 45 | 46 | description.setText(R.string.emoji_download_demo2_description); 47 | 48 | emojiDownloader = new MyEmojiDownloader(); 49 | emojiDownloader.setEmojiDownloadListener(new EmojiDownloader.EmojiDownloadListener() { 50 | @Override 51 | public void emojiDownloadSuccess(EmojiPackage emojiPackage) { 52 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded success!"); 53 | } 54 | 55 | @Override 56 | public void emojiDownloadFailed(EmojiPackage emojiPackage, int errorCode, String errorMessage) { 57 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded failed! errorCode: " + errorCode + ", errorMessage: " + errorMessage); 58 | } 59 | 60 | @Override 61 | public void emojiDownloadProgress(EmojiPackage emojiPackage, String downloadEmojiUrl) { 62 | TextLogUtil.println(logTextView, "progress: " + downloadEmojiUrl + " downloaded..."); 63 | } 64 | }); 65 | 66 | //构造要下载的表情包 67 | EmojiPackage emojiPackage = new EmojiPackage(); 68 | emojiPackage.emojiId = 1002; 69 | emojiPackage.emojiUrls = new ArrayList(); 70 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/1.png"); 71 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/2.png"); 72 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/3.png"); 73 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/4.png"); 74 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/5.png"); 75 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/6.png"); 76 | 77 | TextLogUtil.println(logTextView, "Start downloading emoji package (emojiId: " + emojiPackage.emojiId + ") with " + emojiPackage.emojiUrls.size() + " URLs"); 78 | emojiDownloader.startDownloadEmoji(emojiPackage); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v2/MyEmojiDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji.v2; 17 | 18 | import com.zhangtielei.demos.async.programming.callback.download.v2.DownloadListener; 19 | import com.zhangtielei.demos.async.programming.callback.download.v2.Downloader; 20 | import com.zhangtielei.demos.async.programming.callback.download.v2.MyDownloader; 21 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloadContext; 22 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 24 | 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | /** 30 | * Created by Tielei Zhang on 16/5/1. 31 | * EmojiDownloader的第2个版本的实现: 用映射关系来保存上下文. 32 | */ 33 | public class MyEmojiDownloader implements EmojiDownloader, DownloadListener { 34 | /** 35 | * 保存上下文的映射关系. 36 | * URL -> EmojiDownloadContext 37 | */ 38 | private Map downloadContextMap; 39 | 40 | private Downloader downloader; 41 | private EmojiDownloadListener listener; 42 | 43 | public MyEmojiDownloader() { 44 | downloadContextMap = new HashMap(); 45 | //实例化有一个下载器. 46 | downloader = new MyDownloader(); 47 | downloader.setListener(this); 48 | } 49 | 50 | @Override 51 | public void startDownloadEmoji(EmojiPackage emojiPackage) { 52 | //创建下载上下文数据 53 | EmojiDownloadContext downloadContext = new EmojiDownloadContext(); 54 | downloadContext.emojiPackage = emojiPackage; 55 | //为每一个URL创建映射关系 56 | for (String emojiUrl : emojiPackage.emojiUrls) { 57 | downloadContextMap.put(emojiUrl, downloadContext); 58 | } 59 | //启动第0个表情图片文件的下载 60 | downloader.startDownload(emojiPackage.emojiUrls.get(0), 61 | getLocalPathForEmoji(emojiPackage, 0)); 62 | } 63 | 64 | @Override 65 | public void setEmojiDownloadListener(EmojiDownloadListener listener) { 66 | this.listener = listener; 67 | } 68 | 69 | @Override 70 | public void downloadSuccess(String url, String localPath) { 71 | EmojiDownloadContext downloadContext = downloadContextMap.get(url); 72 | downloadContext.localPathList.add(localPath); 73 | downloadContext.downloadedEmoji++; 74 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 75 | if (downloadContext.downloadedEmoji < emojiPackage.emojiUrls.size()) { 76 | //还没下载完, 产生一个进度回调 77 | try { 78 | if (listener != null) { 79 | listener.emojiDownloadProgress(emojiPackage, url); 80 | } 81 | } 82 | catch (Throwable e) { 83 | e.printStackTrace(); 84 | } 85 | //继续下载下一个表情图片 86 | String nextUrl = emojiPackage.emojiUrls.get(downloadContext.downloadedEmoji); 87 | downloader.startDownload(nextUrl, 88 | getLocalPathForEmoji(emojiPackage, downloadContext.downloadedEmoji)); 89 | } 90 | else { 91 | //已经下载完 92 | installEmojiPackageLocally(emojiPackage, downloadContext.localPathList); 93 | 94 | //为每一个URL删除映射关系 95 | //这个状态清理操作应该在回调之前 96 | for (String emojiUrl : emojiPackage.emojiUrls) { 97 | downloadContextMap.remove(emojiUrl); 98 | } 99 | 100 | //成功回调 101 | try { 102 | if (listener != null) { 103 | listener.emojiDownloadProgress(emojiPackage, url);//最后一次进度回调. 104 | listener.emojiDownloadSuccess(emojiPackage); 105 | } 106 | } 107 | catch (Throwable e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public void downloadFailed(String url, int errorCode, String errorMessage) { 115 | EmojiDownloadContext downloadContext = downloadContextMap.get(url); 116 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 117 | 118 | //为每一个URL删除映射关系 119 | //这个状态清理操作应该在回调之前 120 | for (String emojiUrl : emojiPackage.emojiUrls) { 121 | downloadContextMap.remove(emojiUrl); 122 | } 123 | 124 | //失败回调 125 | try { 126 | if (listener != null) { 127 | listener.emojiDownloadFailed(emojiPackage, errorCode, errorMessage); 128 | } 129 | } 130 | catch (Throwable e) { 131 | e.printStackTrace(); 132 | } 133 | } 134 | 135 | @Override 136 | public void downloadProgress(String url, long downloadedSize, long totalSize) { 137 | //TODO: 138 | } 139 | 140 | /** 141 | * 计算表情包中第i个表情图片文件的下载地址. 142 | * @param emojiPackage 143 | * @param i 144 | * @return 145 | */ 146 | private String getLocalPathForEmoji(EmojiPackage emojiPackage, int i) { 147 | //TODO: 148 | return null; 149 | } 150 | 151 | /** 152 | * 把表情包安装到本地 153 | * @param emojiPackage 154 | * @param localPathList 155 | */ 156 | private void installEmojiPackageLocally(EmojiPackage emojiPackage, List localPathList) { 157 | //TODO: 158 | return; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v3/EmojiDownloadDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.callback.emoji.v3; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 24 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 25 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 26 | 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * 通过为每一个异步任务创建一个接口实例的方式来传递上下文的表情包下载演示页面. 31 | */ 32 | public class EmojiDownloadDemoActivity extends AppCompatActivity { 33 | private TextView description; 34 | private TextView logTextView; 35 | 36 | private EmojiDownloader emojiDownloader; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_log_display); 42 | 43 | description = (TextView) findViewById(R.id.description); 44 | logTextView = (TextView) findViewById(R.id.log_display); 45 | 46 | description.setText(R.string.emoji_download_demo3_description); 47 | 48 | emojiDownloader = new MyEmojiDownloader(); 49 | emojiDownloader.setEmojiDownloadListener(new EmojiDownloader.EmojiDownloadListener() { 50 | @Override 51 | public void emojiDownloadSuccess(EmojiPackage emojiPackage) { 52 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded success!"); 53 | } 54 | 55 | @Override 56 | public void emojiDownloadFailed(EmojiPackage emojiPackage, int errorCode, String errorMessage) { 57 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded failed! errorCode: " + errorCode + ", errorMessage: " + errorMessage); 58 | } 59 | 60 | @Override 61 | public void emojiDownloadProgress(EmojiPackage emojiPackage, String downloadEmojiUrl) { 62 | TextLogUtil.println(logTextView, "progress: " + downloadEmojiUrl + " downloaded..."); 63 | } 64 | }); 65 | 66 | //构造要下载的表情包 67 | EmojiPackage emojiPackage = new EmojiPackage(); 68 | emojiPackage.emojiId = 1003; 69 | emojiPackage.emojiUrls = new ArrayList(); 70 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/1.png"); 71 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/2.png"); 72 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/3.png"); 73 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/4.png"); 74 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/5.png"); 75 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/6.png"); 76 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/7.png"); 77 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/8.png"); 78 | 79 | TextLogUtil.println(logTextView, "Start downloading emoji package (emojiId: " + emojiPackage.emojiId + ") with " + emojiPackage.emojiUrls.size() + " URLs"); 80 | emojiDownloader.startDownloadEmoji(emojiPackage); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v3/MyEmojiDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji.v3; 17 | 18 | import com.zhangtielei.demos.async.programming.callback.download.v2.DownloadListener; 19 | import com.zhangtielei.demos.async.programming.callback.download.v2.MyDownloader; 20 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloadContext; 21 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 22 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * Created by Tielei Zhang on 16/5/1. 28 | * EmojiDownloader的第3个版本的实现: 为每一个异步任务创建一个接口实例. 29 | */ 30 | public class MyEmojiDownloader implements EmojiDownloader { 31 | private EmojiDownloadListener listener; 32 | 33 | @Override 34 | public void startDownloadEmoji(EmojiPackage emojiPackage) { 35 | //创建下载上下文数据 36 | EmojiDownloadContext downloadContext = new EmojiDownloadContext(); 37 | downloadContext.emojiPackage = emojiPackage; 38 | //为每一次下载创建一个新的Downloader 39 | final EmojiUrlDownloader downloader = new EmojiUrlDownloader(); 40 | //将上下文数据存到downloader实例中 41 | downloader.downloadContext = downloadContext; 42 | 43 | downloader.setListener(new DownloadListener() { 44 | @Override 45 | public void downloadSuccess(String url, String localPath) { 46 | EmojiDownloadContext downloadContext = downloader.downloadContext; 47 | downloadContext.localPathList.add(localPath); 48 | downloadContext.downloadedEmoji++; 49 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 50 | if (downloadContext.downloadedEmoji < emojiPackage.emojiUrls.size()) { 51 | //还没下载完, 产生一个进度回调 52 | try { 53 | if (listener != null) { 54 | listener.emojiDownloadProgress(emojiPackage, url); 55 | } 56 | } 57 | catch (Throwable e) { 58 | e.printStackTrace(); 59 | } 60 | //继续下载下一个表情图片 61 | String nextUrl = emojiPackage.emojiUrls.get(downloadContext.downloadedEmoji); 62 | downloader.startDownload(nextUrl, 63 | getLocalPathForEmoji(emojiPackage, downloadContext.downloadedEmoji)); 64 | } 65 | else { 66 | //已经下载完 67 | installEmojiPackageLocally(emojiPackage, downloadContext.localPathList); 68 | 69 | //成功回调 70 | try { 71 | if (listener != null) { 72 | listener.emojiDownloadProgress(emojiPackage, url);//最后一次进度回调. 73 | listener.emojiDownloadSuccess(emojiPackage); 74 | } 75 | } 76 | catch (Throwable e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | 82 | @Override 83 | public void downloadFailed(String url, int errorCode, String errorMessage) { 84 | EmojiDownloadContext downloadContext = downloader.downloadContext; 85 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 86 | 87 | //失败回调 88 | try { 89 | if (listener != null) { 90 | listener.emojiDownloadFailed(emojiPackage, errorCode, errorMessage); 91 | } 92 | } 93 | catch (Throwable e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | 98 | @Override 99 | public void downloadProgress(String url, long downloadedSize, long totalSize) { 100 | //TODO: 101 | } 102 | }); 103 | 104 | //启动第0个表情图片文件的下载 105 | downloader.startDownload(emojiPackage.emojiUrls.get(0), 106 | getLocalPathForEmoji(emojiPackage, 0)); 107 | } 108 | 109 | @Override 110 | public void setEmojiDownloadListener(EmojiDownloadListener listener) { 111 | this.listener = listener; 112 | } 113 | 114 | private static class EmojiUrlDownloader extends MyDownloader { 115 | public EmojiDownloadContext downloadContext; 116 | } 117 | 118 | /** 119 | * 计算表情包中第i个表情图片文件的下载地址. 120 | * @param emojiPackage 121 | * @param i 122 | * @return 123 | */ 124 | private String getLocalPathForEmoji(EmojiPackage emojiPackage, int i) { 125 | //TODO: 126 | return null; 127 | } 128 | 129 | /** 130 | * 把表情包安装到本地 131 | * @param emojiPackage 132 | * @param localPathList 133 | */ 134 | private void installEmojiPackageLocally(EmojiPackage emojiPackage, List localPathList) { 135 | //TODO: 136 | return; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v4/EmojiDownloadDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.callback.emoji.v4; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 24 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 25 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 26 | 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * 利用支持上下文传递的异步接口来实现表情包下载的演示页面. 31 | */ 32 | public class EmojiDownloadDemoActivity extends AppCompatActivity { 33 | private TextView description; 34 | private TextView logTextView; 35 | 36 | private EmojiDownloader emojiDownloader; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_log_display); 42 | 43 | description = (TextView) findViewById(R.id.description); 44 | logTextView = (TextView) findViewById(R.id.log_display); 45 | 46 | description.setText(R.string.emoji_download_demo4_description); 47 | 48 | emojiDownloader = new MyEmojiDownloader(); 49 | emojiDownloader.setEmojiDownloadListener(new EmojiDownloader.EmojiDownloadListener() { 50 | @Override 51 | public void emojiDownloadSuccess(EmojiPackage emojiPackage) { 52 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded success!"); 53 | } 54 | 55 | @Override 56 | public void emojiDownloadFailed(EmojiPackage emojiPackage, int errorCode, String errorMessage) { 57 | TextLogUtil.println(logTextView, "emoji package (emojiId: " + emojiPackage.emojiId + ") downloaded failed! errorCode: " + errorCode + ", errorMessage: " + errorMessage); 58 | } 59 | 60 | @Override 61 | public void emojiDownloadProgress(EmojiPackage emojiPackage, String downloadEmojiUrl) { 62 | TextLogUtil.println(logTextView, "progress: " + downloadEmojiUrl + " downloaded..."); 63 | } 64 | }); 65 | 66 | //构造要下载的表情包 67 | EmojiPackage emojiPackage = new EmojiPackage(); 68 | emojiPackage.emojiId = 1004; 69 | emojiPackage.emojiUrls = new ArrayList(); 70 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/1.png"); 71 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/2.png"); 72 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/3.png"); 73 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/4.png"); 74 | emojiPackage.emojiUrls.add("http://zhangtielei.com/demourls/5.png"); 75 | 76 | TextLogUtil.println(logTextView, "Start downloading emoji package (emojiId: " + emojiPackage.emojiId + ") with " + emojiPackage.emojiUrls.size() + " URLs"); 77 | emojiDownloader.startDownloadEmoji(emojiPackage); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/callback/emoji/v4/MyEmojiDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.callback.emoji.v4; 17 | 18 | 19 | import com.zhangtielei.demos.async.programming.callback.download.v3.DownloadListener; 20 | import com.zhangtielei.demos.async.programming.callback.download.v3.Downloader; 21 | import com.zhangtielei.demos.async.programming.callback.download.v3.MyDownloader; 22 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloadContext; 23 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiDownloader; 24 | import com.zhangtielei.demos.async.programming.callback.emoji.EmojiPackage; 25 | 26 | import java.util.List; 27 | 28 | /** 29 | * Created by Tielei Zhang on 16/5/2. 30 | * EmojiDownloader的第4个版本的实现: 利用支持上下文传递的异步接口. 31 | */ 32 | public class MyEmojiDownloader implements EmojiDownloader, DownloadListener { 33 | private Downloader downloader; 34 | private EmojiDownloadListener listener; 35 | 36 | public MyEmojiDownloader() { 37 | //实例化有一个下载器. 38 | downloader = new MyDownloader(); 39 | downloader.setListener(this); 40 | } 41 | 42 | @Override 43 | public void startDownloadEmoji(EmojiPackage emojiPackage) { 44 | //创建下载上下文数据 45 | EmojiDownloadContext downloadContext = new EmojiDownloadContext(); 46 | downloadContext.emojiPackage = emojiPackage; 47 | //启动第0个表情图片文件的下载, 上下文参数传递进去 48 | downloader.startDownload(emojiPackage.emojiUrls.get(0), 49 | getLocalPathForEmoji(emojiPackage, 0), 50 | downloadContext); 51 | 52 | } 53 | 54 | @Override 55 | public void setEmojiDownloadListener(EmojiDownloadListener listener) { 56 | this.listener = listener; 57 | } 58 | 59 | @Override 60 | public void downloadSuccess(String url, String localPath, Object contextData) { 61 | //通过回调接口的contextData参数做Down-casting获得上下文参数 62 | EmojiDownloadContext downloadContext = (EmojiDownloadContext) contextData; 63 | 64 | downloadContext.localPathList.add(localPath); 65 | downloadContext.downloadedEmoji++; 66 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 67 | if (downloadContext.downloadedEmoji < emojiPackage.emojiUrls.size()) { 68 | //还没下载完, 产生一个进度回调 69 | try { 70 | if (listener != null) { 71 | listener.emojiDownloadProgress(emojiPackage, url); 72 | } 73 | } 74 | catch (Throwable e) { 75 | e.printStackTrace(); 76 | } 77 | //继续下载下一个表情图片 78 | String nextUrl = emojiPackage.emojiUrls.get(downloadContext.downloadedEmoji); 79 | downloader.startDownload(nextUrl, 80 | getLocalPathForEmoji(emojiPackage, downloadContext.downloadedEmoji), 81 | downloadContext); 82 | } 83 | else { 84 | //已经下载完 85 | installEmojiPackageLocally(emojiPackage, downloadContext.localPathList); 86 | 87 | //成功回调 88 | try { 89 | if (listener != null) { 90 | listener.emojiDownloadProgress(emojiPackage, url);//最后一次进度回调. 91 | listener.emojiDownloadSuccess(emojiPackage); 92 | } 93 | } 94 | catch (Throwable e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | 100 | @Override 101 | public void downloadFailed(String url, int errorCode, String errorMessage, Object contextData) { 102 | //通过回调接口的contextData参数做Down-casting获得上下文参数 103 | EmojiDownloadContext downloadContext = (EmojiDownloadContext) contextData; 104 | EmojiPackage emojiPackage = downloadContext.emojiPackage; 105 | 106 | //失败回调 107 | try { 108 | if (listener != null) { 109 | listener.emojiDownloadFailed(emojiPackage, errorCode, errorMessage); 110 | } 111 | } 112 | catch (Throwable e) { 113 | e.printStackTrace(); 114 | } 115 | } 116 | 117 | @Override 118 | public void downloadProgress(String url, long downloadedSize, long totalSize, Object contextData) { 119 | //TODO: 120 | } 121 | 122 | /** 123 | * 计算表情包中第i个表情图片文件的下载地址. 124 | * @param emojiPackage 125 | * @param i 126 | * @return 127 | */ 128 | private String getLocalPathForEmoji(EmojiPackage emojiPackage, int i) { 129 | //TODO: 130 | return null; 131 | } 132 | 133 | /** 134 | * 把表情包安装到本地 135 | * @param emojiPackage 136 | * @param localPathList 137 | */ 138 | private void installEmojiPackageLocally(EmojiPackage emojiPackage, List localPathList) { 139 | //TODO: 140 | return; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/common/AsyncCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.common; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | * 22 | * 一个通用的回调接口定义. 用于返回一个参数. 23 | * @param 异步接口返回的参数数据类型. 24 | */ 25 | public interface AsyncCallback { 26 | void onResult(D data); 27 | } 28 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/common/utils/TextLogUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.common.utils; 18 | 19 | import android.widget.TextView; 20 | 21 | import java.text.SimpleDateFormat; 22 | import java.util.Date; 23 | 24 | /** 25 | * Created by Tielei Zhang on 16/8/16. 26 | * 一个工具类, 用于在页面上打印日志. 27 | */ 28 | public class TextLogUtil { 29 | /** 30 | * 在一个TextView上追加一行日志. 31 | * @param logTextView 32 | * @param log 33 | */ 34 | public static void println(TextView logTextView, CharSequence log) { 35 | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); 36 | String timestamp = sdf.format(new Date()); 37 | 38 | CharSequence newText = timestamp + ": " + log; 39 | CharSequence oldText = logTextView.getText(); 40 | if (oldText == null || oldText.length() <= 0) { 41 | logTextView.setText(newText); 42 | } 43 | else { 44 | logTextView.setText(oldText + "\n" + newText); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/introduction/servicebinding/ServiceBindingDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.introduction.servicebinding; 18 | 19 | import android.content.ComponentName; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.ServiceConnection; 23 | import android.os.Bundle; 24 | import android.os.IBinder; 25 | import android.support.v7.app.AppCompatActivity; 26 | import android.widget.TextView; 27 | import com.zhangtielei.demos.async.programming.R; 28 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 29 | import com.zhangtielei.demos.async.programming.introduction.servicebinding.service.SomeService; 30 | 31 | /** 32 | * 用于演示最普通的Service绑定和解绑. 33 | */ 34 | public class ServiceBindingDemoActivity extends AppCompatActivity implements SomeService.ServiceListener { 35 | /** 36 | * 对于Service的引用 37 | */ 38 | private SomeService someService; 39 | /** 40 | * 指示本Activity是否处于running状态:执行过onResume就变为running状态。 41 | */ 42 | private boolean running; 43 | 44 | private TextView description; 45 | private TextView logTextView; 46 | 47 | private ServiceConnection serviceConnection = new ServiceConnection() { 48 | @Override 49 | public void onServiceDisconnected(ComponentName name) { 50 | //解除Activity与Service的引用和监听关系 51 | //... 52 | if (someService != null) { 53 | someService.removeListener(ServiceBindingDemoActivity.this); 54 | someService = null; 55 | } 56 | TextLogUtil.println(logTextView, "disconnected to SomeService..."); 57 | } 58 | 59 | @Override 60 | public void onServiceConnected(ComponentName name, IBinder service) { 61 | if (running) { 62 | //建立Activity与Service的引用和监听关系 63 | //... 64 | SomeService.ServiceBinder binder = (SomeService.ServiceBinder) service; 65 | someService = binder.getService(); 66 | someService.addListener(ServiceBindingDemoActivity.this); 67 | TextLogUtil.println(logTextView, "bound to SomeService..."); 68 | } 69 | } 70 | }; 71 | 72 | @Override 73 | protected void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | setContentView(R.layout.activity_log_display); 76 | 77 | description = (TextView) findViewById(R.id.description); 78 | logTextView = (TextView) findViewById(R.id.log_display); 79 | 80 | description.setText(R.string.service_binding_description); 81 | 82 | } 83 | 84 | @Override 85 | public void onResume() { 86 | super.onResume(); 87 | running = true; 88 | 89 | Intent intent = new Intent(this, SomeService.class); 90 | bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); 91 | TextLogUtil.println(logTextView, "Requesting bind to SomeService..."); 92 | } 93 | 94 | @Override 95 | public void onPause() { 96 | super.onPause(); 97 | running = false; 98 | 99 | //解除Activity与Service的引用和监听关系 100 | //... 101 | if (someService != null) { 102 | someService.removeListener(ServiceBindingDemoActivity.this); 103 | someService = null; 104 | } 105 | unbindService(serviceConnection); 106 | TextLogUtil.println(logTextView, "unbind to SomeService..."); 107 | } 108 | 109 | @Override 110 | public void callback() { 111 | //TODO: 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/introduction/servicebinding/service/SomeService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.introduction.servicebinding.service; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.os.Binder; 22 | import android.os.IBinder; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.Random; 27 | 28 | /** 29 | * 一个Service例子, 用于演示Service Binding中存在的异步问题. 30 | */ 31 | public class SomeService extends Service { 32 | private static final String TAG = "SomeService"; 33 | 34 | public SomeService() { 35 | } 36 | 37 | /** 38 | * 用于OnBind返回的Binder实例 39 | */ 40 | private ServiceBinder binder = new ServiceBinder(); 41 | private List listeners = new ArrayList(); 42 | 43 | private Random random = new Random(); 44 | 45 | @Override 46 | public IBinder onBind(Intent intent) { 47 | // Return the communication channel to the service. 48 | return binder; 49 | } 50 | 51 | public void addListener(ServiceListener listener) { 52 | if (!listeners.contains(listener)) { 53 | listeners.add(listener); 54 | } 55 | } 56 | 57 | public void removeListener(ServiceListener listener) { 58 | listeners.remove(listener); 59 | } 60 | 61 | /** 62 | * 与此Service通信的Binder类 63 | */ 64 | public class ServiceBinder extends Binder { 65 | public SomeService getService() { 66 | return SomeService.this; 67 | } 68 | } 69 | 70 | /** 71 | * 此Service对外界的回调接口定义. 72 | */ 73 | public interface ServiceListener { 74 | void callback(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/MultiTaskDemoListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask; 18 | 19 | import android.content.Intent; 20 | import android.content.res.Resources; 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.view.View; 24 | import android.widget.AdapterView; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ListAdapter; 27 | import android.widget.ListView; 28 | import com.zhangtielei.demos.async.programming.R; 29 | import com.zhangtielei.demos.async.programming.multitask.multilevelcaching.ImageLoaderDemoActivity; 30 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.PageCachingDemoActivity; 31 | 32 | /** 33 | * 多个任务同时执行的演示程序入口列表页面. 34 | */ 35 | public class MultiTaskDemoListActivity extends AppCompatActivity { 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_item_list); 41 | 42 | ListView listView = (ListView) findViewById(R.id.item_list); 43 | Resources resources = getResources(); 44 | String[] textList = new String[] { 45 | resources.getString(R.string.multitask_demo_1), 46 | resources.getString(R.string.multitask_demo_2), 47 | resources.getString(R.string.multitask_demo_3), 48 | resources.getString(R.string.multitask_demo_4), 49 | }; 50 | ListAdapter listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, textList); 51 | listView.setAdapter(listAdapter); 52 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 53 | @Override 54 | public void onItemClick(AdapterView parent, View view, int position, long id) { 55 | switch (position) { 56 | case 0: 57 | { 58 | Intent intent = new Intent(MultiTaskDemoListActivity.this, ImageLoaderDemoActivity.class); 59 | startActivity(intent); 60 | break; 61 | } 62 | case 1: 63 | { 64 | Intent intent = new Intent(MultiTaskDemoListActivity.this, com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.MultiRequestsDemoActivity.class); 65 | startActivity(intent); 66 | break; 67 | } 68 | case 2: 69 | { 70 | Intent intent = new Intent(MultiTaskDemoListActivity.this, PageCachingDemoActivity.class); 71 | startActivity(intent); 72 | break; 73 | } 74 | case 3: 75 | { 76 | Intent intent = new Intent(MultiTaskDemoListActivity.this, com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.rxjavasolution.MultiRequestsDemoActivity.class); 77 | startActivity(intent); 78 | break; 79 | } 80 | default: 81 | break; 82 | } 83 | } 84 | }); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/http/HttpListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.multitask.http; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/16. 20 | * 监听Http服务的监听器接口. 21 | * 22 | * @param 请求Model类型 23 | * @param 响应Model类型 24 | */ 25 | public interface HttpListener { 26 | /** 27 | * 产生请求结果(成功或失败)时的回调接口. 28 | * @param apiUrl 请求URL 29 | * @param request 请求Model 30 | * @param result 请求结果(包括响应或者错误原因) 31 | * @param contextData 透传参数 32 | */ 33 | void onResult(String apiUrl, T request, HttpResult result, Object contextData); 34 | } 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/http/HttpResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.multitask.http; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/16. 20 | * HttpResult封装Http请求的结果. 21 | * 22 | * 当服务器成功响应的时候, errorCode = SUCCESS, 且服务器的响应转换成response; 23 | * 当服务器未能成功响应的时候, errorCode != SUCCESS, 且response的值无效. 24 | * 25 | * @param 响应Model类型 26 | */ 27 | public class HttpResult { 28 | /** 29 | * 错误码定义 30 | */ 31 | public static final int SUCCESS = 0;//成功 32 | public static final int REQUEST_ENCODING_ERROR = 1;//对请求进行编码发生错误 33 | public static final int RESPONSE_DECODING_ERROR = 2;//对响应进行解码发生错误 34 | public static final int NETWORK_UNAVAILABLE = 3;//网络不可用 35 | public static final int UNKNOWN_HOST = 4;//域名解析失败 36 | public static final int CONNECT_TIMEOUT = 5;//连接超时 37 | public static final int HTTP_STATUS_NOT_OK = 6;//下载请求返回非200 38 | public static final int UNKNOWN_FAILED = 7;//其它未知错误 39 | 40 | private int errorCode; 41 | private String errorMessage; 42 | /** 43 | * response是服务器返回的响应. 44 | * 只有当errorCode = SUCCESS, response的值才有效. 45 | */ 46 | private R response; 47 | 48 | public int getErrorCode() { 49 | return errorCode; 50 | } 51 | 52 | public void setErrorCode(int errorCode) { 53 | this.errorCode = errorCode; 54 | } 55 | 56 | public String getErrorMessage() { 57 | return errorMessage; 58 | } 59 | 60 | public void setErrorMessage(String errorMessage) { 61 | this.errorMessage = errorMessage; 62 | } 63 | 64 | public R getResponse() { 65 | return response; 66 | } 67 | 68 | public void setResponse(R response) { 69 | this.response = response; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/http/HttpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.multitask.http; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/16. 20 | * 21 | * Http服务请求接口. 22 | */ 23 | public interface HttpService { 24 | /** 25 | * 发起HTTP请求. 26 | * @param apiUrl 请求URL 27 | * @param request 请求参数(用Java Bean表示) 28 | * @param listener 回调监听器 29 | * @param contextData 透传参数 30 | * @param 请求Model类型 31 | * @param 响应Model类型 32 | */ 33 | void doRequest(String apiUrl, T request, HttpListener listener, Object contextData); 34 | } 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/http/mock/MockHttpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.http.mock; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import com.zhangtielei.demos.async.programming.multitask.http.HttpListener; 22 | import com.zhangtielei.demos.async.programming.multitask.http.HttpResult; 23 | import com.zhangtielei.demos.async.programming.multitask.http.HttpService; 24 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpRequest; 25 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpResponse; 26 | import com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model.HttpRequest1; 27 | import com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model.HttpRequest2; 28 | import com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model.HttpResponse1; 29 | import com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model.HttpResponse2; 30 | 31 | import java.util.Date; 32 | import java.util.Random; 33 | import java.util.concurrent.ExecutorService; 34 | import java.util.concurrent.Executors; 35 | import java.util.concurrent.TimeUnit; 36 | 37 | /** 38 | * Created by Tielei Zhang on 16/5/17. 39 | * 40 | * HttpService的一个假的实现. 41 | */ 42 | public class MockHttpService implements HttpService { 43 | private ExecutorService executorService = Executors.newCachedThreadPool(); 44 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 45 | private Random rand = new Random(); 46 | 47 | @Override 48 | public void doRequest(final String apiUrl, final T request, final HttpListener listener, final Object contextData) { 49 | executorService.execute(new Runnable() { 50 | @Override 51 | public void run() { 52 | //模拟请求执行, 随机执行0~3秒 53 | try { 54 | TimeUnit.MILLISECONDS.sleep(rand.nextInt(3000)); 55 | } catch (InterruptedException e) { 56 | e.printStackTrace(); 57 | } 58 | 59 | //模拟一个返回结果 60 | Object httpResponse = generateResponse(request); 61 | final HttpResult result = new HttpResult(); 62 | result.setErrorCode(HttpResult.SUCCESS); 63 | result.setResponse((R) httpResponse); 64 | 65 | //回调 66 | mainHandler.post(new Runnable() { 67 | @Override 68 | public void run() { 69 | if (listener != null) { 70 | try { 71 | listener.onResult(apiUrl, request, result, contextData); 72 | } 73 | catch (Throwable e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | } 78 | }); 79 | } 80 | }); 81 | } 82 | 83 | private Object generateResponse(Object httpRequest) { 84 | //hard code 85 | if (httpRequest instanceof HttpRequest1) { 86 | HttpResponse1 response1 = new HttpResponse1(); 87 | response1.setText("Data from HttpRequest1. timestamp: " + new Date()); 88 | return response1; 89 | } 90 | else if (httpRequest instanceof HttpRequest2) { 91 | HttpResponse2 response2 = new HttpResponse2(); 92 | response2.setText("Data from HttpRequest2. timestamp: " + new Date()); 93 | return response2; 94 | } 95 | else if (httpRequest instanceof HttpRequest) { 96 | HttpResponse response = new HttpResponse(); 97 | return response; 98 | } 99 | return null; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/ImageLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | * 一个简单的图片加载器. 22 | * 两级Cache: memory cache, disk cache; 如果都没有命中, 则调用Downloader去下载. 23 | * 注意: 这个实现只是一个Demo, 为了演示多级缓存用. 24 | */ 25 | public interface ImageLoader { 26 | /** 27 | * 设置回调监听器. 28 | * @param listener 29 | */ 30 | void setListener(ImageLoaderListener listener); 31 | /** 32 | * 根据指定Url启动图片加载. 33 | * @param url 要下载的资源地址. 34 | * @param contextData 上下文数据, 在回调接口中会透传回去.可以是任何类型. 35 | */ 36 | void startImageLoad(String url, Object contextData); 37 | } 38 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/ImageLoaderDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching; 18 | 19 | import android.graphics.Bitmap; 20 | import android.os.Bundle; 21 | import android.support.v7.app.AppCompatActivity; 22 | import android.widget.TextView; 23 | import com.zhangtielei.demos.async.programming.R; 24 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 25 | import com.zhangtielei.demos.async.programming.multitask.multilevelcaching.mock.MyDemoImageLoader; 26 | 27 | /** 28 | * 一个演示页面: 调用一个带多级缓存的图片下载器, 演示"多个异步任务先后接续执行". 29 | */ 30 | public class ImageLoaderDemoActivity extends AppCompatActivity { 31 | private TextView description; 32 | private TextView logTextView; 33 | 34 | private ImageLoader imageLoader; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_log_display); 40 | 41 | description = (TextView) findViewById(R.id.description); 42 | logTextView = (TextView) findViewById(R.id.log_display); 43 | 44 | description.setText(R.string.imageloader_demo_description); 45 | 46 | imageLoader = new MyDemoImageLoader(); 47 | imageLoader.setListener(new ImageLoaderListener() { 48 | @Override 49 | public void imageLoadSuccess(String url, Bitmap bitmap, Object contextData) { 50 | TextLogUtil.println(logTextView, "Loaded image(" + bitmap + ") from: " + url + ", with context data: " + contextData); 51 | } 52 | 53 | @Override 54 | public void imageLoadFailed(String url, int errorCode, String errorMessage, Object contextData) { 55 | TextLogUtil.println(logTextView, "Load image failed from: " + url + ", with context data: " + contextData + ", errorCode: " + errorCode + ", errorMessage: " + errorMessage); 56 | } 57 | }); 58 | 59 | //发起请求: 请求一个静态图片 60 | String imageUrl = "http://zhangtielei.com/demourls/1.png"; 61 | Object contextData = "Here ImageLoaderDemoActivity"; 62 | TextLogUtil.println(logTextView, "Start to download image from: " + imageUrl + ", with request context data: " + contextData); 63 | imageLoader.startImageLoad(imageUrl, contextData); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/ImageLoaderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching; 18 | 19 | import android.graphics.Bitmap; 20 | 21 | /** 22 | * Created by Tielei Zhang on 16/5/17. 23 | */ 24 | public interface ImageLoaderListener { 25 | /** 26 | * 错误码定义 27 | */ 28 | int SUCCESS = 0;//成功 29 | int BITMAP_DECODE_FAILED = 1;//图片解码失败 30 | int NETWORK_UNAVAILABLE = 2;//网络不可用 31 | int SDCARD_NOT_EXISTS = 3;//SD卡不存在(下载的图片文件没地方存) 32 | int SD_CARD_NO_SPACE_LEFT = 4;//SD卡空间不足(下载的图片文件没地方存) 33 | int DOWNLOAD_FAILED = 5;//图片文件下载过程出错 34 | int UNKNOWN_FAILED = 6;//其它未知错误 35 | 36 | /** 37 | * 图片加载成功回调. 38 | * @param url 图片地址 39 | * @param bitmap 下载到的Bitmap对象. 40 | * @param contextData 上下文数据. 41 | */ 42 | void imageLoadSuccess(String url, Bitmap bitmap, Object contextData); 43 | /** 44 | * 图片加载失败回调. 45 | * @param url 图片地址 46 | * @param errorCode 错误码. 47 | * @param errorMessage 错误信息简短描述. 供调用者理解错误原因. 48 | * @param contextData 上下文数据. 49 | */ 50 | void imageLoadFailed(String url, int errorCode, String errorMessage, Object contextData); 51 | } 52 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/diskcache/ImageDiskCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching.diskcache; 18 | 19 | import android.graphics.Bitmap; 20 | import com.zhangtielei.demos.async.programming.common.AsyncCallback; 21 | 22 | /** 23 | * Created by Tielei Zhang on 16/5/17. 24 | * 25 | * 图片的Disk缓存. 26 | * 异步接口形式. 27 | */ 28 | public interface ImageDiskCache { 29 | /** 30 | * 异步获取缓存的Bitmap对象. 31 | * @param key 32 | * @param callback 用于返回缓存的Bitmap对象 33 | */ 34 | void getImage(String key, AsyncCallback callback); 35 | /** 36 | * 保存Bitmap对象到缓存中. 37 | * @param key 38 | * @param bitmap 要保存的Bitmap对象 39 | * @param callback 用于返回当前保存操作的结果是成功还是失败. 40 | */ 41 | void putImage(String key, Bitmap bitmap, AsyncCallback callback); 42 | } 43 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/diskcache/mock/MockImageDiskCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching.diskcache.mock; 18 | 19 | import android.graphics.Bitmap; 20 | import android.os.Handler; 21 | import android.os.Looper; 22 | import com.zhangtielei.demos.async.programming.common.AsyncCallback; 23 | import com.zhangtielei.demos.async.programming.multitask.multilevelcaching.diskcache.ImageDiskCache; 24 | 25 | import java.util.Random; 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * Created by Tielei Zhang on 16/5/17. 32 | */ 33 | public class MockImageDiskCache implements ImageDiskCache { 34 | private ExecutorService executorService = Executors.newCachedThreadPool(); 35 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 36 | private Random rand = new Random(); 37 | 38 | @Override 39 | public void getImage(String key, final AsyncCallback callback) { 40 | executorService.execute(new Runnable() { 41 | @Override 42 | public void run() { 43 | //模拟请求执行, 随机执行0~1秒 44 | try { 45 | TimeUnit.MILLISECONDS.sleep(rand.nextInt(1000)); 46 | } catch (InterruptedException e) { 47 | e.printStackTrace(); 48 | } 49 | 50 | //模拟一个下载成功回调 51 | mainHandler.post(new Runnable() { 52 | @Override 53 | public void run() { 54 | try { 55 | if (callback != null) { 56 | //TODO: 这里写死返回null, 模拟Disk缓存没有命中的情形 57 | callback.onResult(null); 58 | } 59 | } 60 | catch (Throwable e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | }); 65 | } 66 | }); 67 | } 68 | 69 | @Override 70 | public void putImage(String key, Bitmap bitmap, AsyncCallback callback) { 71 | //TODO: 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/memcache/ImageMemCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching.memcache; 18 | 19 | import android.graphics.Bitmap; 20 | 21 | /** 22 | * Created by Tielei Zhang on 16/5/17. 23 | * 24 | * 图片Memory缓存. 25 | * 由于是内存操作, 采用同步接口. 26 | */ 27 | public interface ImageMemCache { 28 | Bitmap getImage(String key); 29 | boolean putImage(String key, Bitmap bitmap); 30 | } 31 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/multilevelcaching/memcache/mock/MockImageMemCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.multilevelcaching.memcache.mock; 18 | 19 | import android.graphics.Bitmap; 20 | import com.zhangtielei.demos.async.programming.multitask.multilevelcaching.memcache.ImageMemCache; 21 | 22 | /** 23 | * Created by Tielei Zhang on 16/5/17. 24 | */ 25 | public class MockImageMemCache implements ImageMemCache { 26 | @Override 27 | public Bitmap getImage(String key) { 28 | //TODO: 29 | //返回null表示没有命中 30 | return null; 31 | } 32 | 33 | @Override 34 | public boolean putImage(String key, Bitmap bitmap) { 35 | //TODO: 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/pagecaching/PageCachingDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.pagecaching; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.common.AsyncCallback; 24 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 25 | import com.zhangtielei.demos.async.programming.multitask.http.HttpListener; 26 | import com.zhangtielei.demos.async.programming.multitask.http.HttpResult; 27 | import com.zhangtielei.demos.async.programming.multitask.http.HttpService; 28 | import com.zhangtielei.demos.async.programming.multitask.http.mock.MockHttpService; 29 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.localcache.LocalDataCache; 30 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.localcache.mock.MockLocalDataCache; 31 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpRequest; 32 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpResponse; 33 | 34 | /** 35 | * 演示Page Caching的异步处理过程. 36 | * 37 | * Page Caching策略: 页面打开后先展示本地缓存数据, 然后API请求返回数据后, 再重新刷新页面. 38 | */ 39 | public class PageCachingDemoActivity extends AppCompatActivity { 40 | private TextView description; 41 | private TextView logTextView; 42 | 43 | private HttpService httpService = new MockHttpService(); 44 | private LocalDataCache localDataCache = new MockLocalDataCache(); 45 | /** 46 | * 从Http请求到的数据是否已经返回 47 | */ 48 | private boolean dataFromHttpReady; 49 | 50 | @Override 51 | protected void onCreate(Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_log_display); 54 | 55 | description = (TextView) findViewById(R.id.description); 56 | logTextView = (TextView) findViewById(R.id.log_display); 57 | 58 | description.setText(R.string.pagecaching_demo_description); 59 | 60 | //同时发起本地数据请求和远程Http请求 61 | final String userId = "xxx"; 62 | TextLogUtil.println(logTextView, "Start requesting local data cache..."); 63 | localDataCache.getCachingData(userId, new AsyncCallback() { 64 | @Override 65 | public void onResult(HttpResponse data) { 66 | TextLogUtil.println(logTextView, "Data from local data cache: " + data); 67 | 68 | if (data != null && !dataFromHttpReady) { 69 | //缓存有旧数据 & 远程Http请求还没返回,先显示旧数据 70 | processData(data); 71 | } 72 | else if (data != null && dataFromHttpReady) { 73 | //只是打印一行日志 74 | TextLogUtil.println(logTextView, "Data from local data cache is ignored."); 75 | } 76 | } 77 | }); 78 | TextLogUtil.println(logTextView, "Start requesting HTTP..."); 79 | httpService.doRequest("http://...", new HttpRequest(), 80 | new HttpListener() { 81 | @Override 82 | public void onResult(String apiUrl, 83 | HttpRequest request, 84 | HttpResult result, 85 | Object contextData) { 86 | TextLogUtil.println(logTextView, "Data from HTTP: " + result.getResponse()); 87 | if (result.getErrorCode() == HttpResult.SUCCESS) { 88 | dataFromHttpReady = true; 89 | processData(result.getResponse()); 90 | //从Http拉到最新数据, 更新本地缓存 91 | localDataCache.putCachingData(userId, result.getResponse(), null); 92 | } 93 | else { 94 | processError(result.getErrorCode()); 95 | } 96 | } 97 | }, 98 | null); 99 | } 100 | 101 | 102 | private void processData(HttpResponse data) { 103 | TextLogUtil.println(logTextView, "Got data success: " + data); 104 | } 105 | 106 | private void processError(int errorCode) { 107 | TextLogUtil.println(logTextView, "Got data failed. errorCode: " + errorCode); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/pagecaching/localcache/LocalDataCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.pagecaching.localcache; 18 | 19 | import com.zhangtielei.demos.async.programming.common.AsyncCallback; 20 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpResponse; 21 | 22 | /** 23 | * Created by Tielei Zhang on 16/5/17. 24 | * 本地数据缓存. 25 | */ 26 | public interface LocalDataCache { 27 | /** 28 | * 异步获取本地缓存的HttpResponse对象. 29 | * @param key 30 | * @param callback 用于返回缓存对象 31 | */ 32 | void getCachingData(String key, AsyncCallback callback); 33 | 34 | /** 35 | * 保存HttpResponse对象到缓存中. 36 | * @param key 37 | * @param data 要保存的HttpResponse对象 38 | * @param callback 用于返回当前保存操作的结果是成功还是失败. 39 | */ 40 | void putCachingData(String key, HttpResponse data, AsyncCallback callback); 41 | } 42 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/pagecaching/localcache/mock/MockLocalDataCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.pagecaching.localcache.mock; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import com.zhangtielei.demos.async.programming.common.AsyncCallback; 22 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.localcache.LocalDataCache; 23 | import com.zhangtielei.demos.async.programming.multitask.pagecaching.model.HttpResponse; 24 | 25 | import java.util.Random; 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * Created by Tielei Zhang on 16/5/17. 32 | * 本地页面Cache的一个假的实现. 33 | */ 34 | public class MockLocalDataCache implements LocalDataCache { 35 | private ExecutorService executorService = Executors.newCachedThreadPool(); 36 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 37 | private Random rand = new Random(); 38 | 39 | @Override 40 | public void getCachingData(String key, final AsyncCallback callback) { 41 | executorService.execute(new Runnable() { 42 | @Override 43 | public void run() { 44 | //模拟请求执行, 随机执行0~1秒 45 | try { 46 | TimeUnit.MILLISECONDS.sleep(rand.nextInt(1000)); 47 | } catch (InterruptedException e) { 48 | e.printStackTrace(); 49 | } 50 | 51 | //回调 52 | mainHandler.post(new Runnable() { 53 | @Override 54 | public void run() { 55 | if (callback != null) { 56 | try { 57 | //模拟命中还是不命中的两种情况: 以80%概率命中 58 | HttpResponse httpResponse = (rand.nextInt(10) <= 7) ? new HttpResponse() : null; 59 | callback.onResult(httpResponse); 60 | } 61 | catch (Throwable e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | }); 67 | } 68 | }); 69 | } 70 | 71 | @Override 72 | public void putCachingData(String key, HttpResponse data, AsyncCallback callback) { 73 | //TODO: 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/pagecaching/model/HttpRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.pagecaching.model; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | */ 22 | public class HttpRequest { 23 | } 24 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/pagecaching/model/HttpResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.pagecaching.model; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | */ 22 | public class HttpResponse { 23 | } 24 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/simultaneousrequests/model/HttpRequest1.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/17. 20 | */ 21 | public class HttpRequest1 { 22 | } 23 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/simultaneousrequests/model/HttpRequest2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | */ 22 | public class HttpRequest2 { 23 | } 24 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/simultaneousrequests/model/HttpResponse1.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model; 17 | 18 | /** 19 | * Created by Tielei Zhang on 16/5/17. 20 | */ 21 | public class HttpResponse1 { 22 | private String text; 23 | 24 | public String getText() { 25 | return text; 26 | } 27 | 28 | public void setText(String text) { 29 | this.text = text; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/multitask/simultaneousrequests/model/HttpResponse2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.multitask.simultaneousrequests.model; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/5/17. 21 | */ 22 | public class HttpResponse2 { 23 | private String text; 24 | 25 | public String getText() { 26 | return text; 27 | } 28 | 29 | public void setText(String text) { 30 | this.text = text; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/QueueingDemoListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing; 18 | 19 | import android.content.Intent; 20 | import android.content.res.Resources; 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.view.View; 24 | import android.widget.AdapterView; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ListAdapter; 27 | import android.widget.ListView; 28 | import com.zhangtielei.demos.async.programming.R; 29 | import com.zhangtielei.demos.async.programming.queueing.v1.TaskQueueDemoActivity; 30 | 31 | public class QueueingDemoListActivity extends AppCompatActivity { 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_item_list); 37 | 38 | ListView listView = (ListView) findViewById(R.id.item_list); 39 | Resources resources = getResources(); 40 | String[] textList = new String[] { 41 | resources.getString(R.string.queueing_demo_1), 42 | resources.getString(R.string.queueing_demo_2), 43 | resources.getString(R.string.queueing_demo_3), 44 | }; 45 | ListAdapter listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, textList); 46 | listView.setAdapter(listAdapter); 47 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 48 | @Override 49 | public void onItemClick(AdapterView parent, View view, int position, long id) { 50 | switch (position) { 51 | case 0: 52 | { 53 | Intent intent = new Intent(QueueingDemoListActivity.this, TaskQueueDemoActivity.class); 54 | startActivity(intent); 55 | break; 56 | } 57 | case 1: 58 | { 59 | Intent intent = new Intent(QueueingDemoListActivity.this, com.zhangtielei.demos.async.programming.queueing.v2.TaskQueueDemoActivity.class); 60 | startActivity(intent); 61 | break; 62 | } 63 | case 2: 64 | { 65 | Intent intent = new Intent(QueueingDemoListActivity.this, com.zhangtielei.demos.async.programming.queueing.v3.TaskQueueDemoActivity.class); 66 | startActivity(intent); 67 | break; 68 | } 69 | default: 70 | break; 71 | } 72 | } 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v1/TSQBasedTaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v1; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import android.util.Log; 22 | 23 | import java.util.concurrent.BlockingQueue; 24 | import java.util.concurrent.LinkedBlockingQueue; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | 27 | /** 28 | * Created by Tielei Zhang on 16/8/31. 29 | * 30 | * 基于TSQ (Thread-Safe Queue)的队列实现 31 | */ 32 | public class TSQBasedTaskQueue implements TaskQueue { 33 | private static final String TAG = "TaskQueue"; 34 | 35 | private BlockingQueue taskBlockingQueue = new LinkedBlockingQueue(); 36 | private Thread consumerThread; 37 | private AtomicBoolean stopped = new AtomicBoolean(false); 38 | 39 | private TaskQueueListener listener; 40 | 41 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 42 | 43 | /** 44 | * 一个任务最多重试次数. 45 | * 重试次数超过MAX_RETRIES, 任务则最终失败. 46 | */ 47 | private static final int MAX_RETRIES = 3; 48 | 49 | public TSQBasedTaskQueue() { 50 | //创建消费者线程 51 | consumerThread = new Thread() { 52 | @Override 53 | public void run() { 54 | while (!stopped.get() && !Thread.interrupted()) { 55 | try { 56 | Task task = taskBlockingQueue.take(); 57 | 58 | int retryCount = 0; 59 | boolean runSuccess = false; 60 | Throwable error = null; 61 | while (!stopped.get() && retryCount < MAX_RETRIES && !runSuccess) { 62 | try { 63 | task.run(); 64 | //任务成功执行完了 65 | runSuccess = true; 66 | } 67 | catch (Throwable e) { 68 | error = e; 69 | } 70 | retryCount++; 71 | } 72 | 73 | if (runSuccess) { 74 | callbackSuccess(task); 75 | } 76 | else { 77 | callbackFailed(task, error); 78 | } 79 | 80 | } catch (InterruptedException e) { 81 | //退出了 82 | } 83 | } 84 | Log.i(TAG, "TSQ thread exit..."); 85 | } 86 | }; 87 | consumerThread.start(); 88 | } 89 | 90 | @Override 91 | public void addTask(Task task) { 92 | try { 93 | taskBlockingQueue.put(task); 94 | } catch (InterruptedException e) { 95 | callbackFailed(task, e); 96 | Log.e(TAG, "", e); 97 | } 98 | } 99 | 100 | @Override 101 | public void setListener(TaskQueueListener listener) { 102 | this.listener = listener; 103 | } 104 | 105 | @Override 106 | public void destroy() { 107 | stopped.set(true); 108 | consumerThread.interrupt(); 109 | } 110 | 111 | private void callbackSuccess(Task task) { 112 | callbackToListener(task, null); 113 | } 114 | 115 | private void callbackFailed(Task task, Throwable error) { 116 | callbackToListener(task, error); 117 | } 118 | 119 | private void callbackToListener(final Task task, final Throwable error) { 120 | mainHandler.post(new Runnable() { 121 | @Override 122 | public void run() { 123 | try { 124 | if (listener != null) { 125 | if (error == null) { 126 | listener.taskComplete(task); 127 | } 128 | else { 129 | listener.taskFailed(task, error); 130 | } 131 | } 132 | } 133 | catch (Throwable e) { 134 | Log.e(TAG, "", e); 135 | } 136 | } 137 | }); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v1/Task.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v1; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/8/31. 21 | * 22 | * 供任务队列执行的同步任务接口定义. 23 | */ 24 | public interface Task { 25 | /** 26 | * 唯一标识当前任务的ID 27 | * @return 28 | */ 29 | String getTaskId(); 30 | 31 | /** 32 | * 同步执行任务. 33 | * 如果执行出错, 可以在处理过程中按异常抛出; 若没抛异常,则认为是执行成功. 34 | * 35 | * 注: run方法在异步线程上执行. 36 | */ 37 | void run(); 38 | } 39 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v1/TaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v1; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/8/31. 21 | * 任务队列接口. 22 | * 23 | * 第一版. 24 | */ 25 | public interface TaskQueue { 26 | /** 27 | * 向队列中添加一个任务. 28 | * @param task 29 | */ 30 | void addTask(Task task); 31 | 32 | /** 33 | * 设置监听器. 34 | * @param listener 35 | */ 36 | void setListener(TaskQueueListener listener); 37 | 38 | /** 39 | * 销毁队列. 40 | * 注: 队列在最后不用的时候, 应该主动销毁它. 41 | */ 42 | void destroy(); 43 | 44 | /** 45 | * 任务队列对外监听接口. 46 | */ 47 | interface TaskQueueListener { 48 | /** 49 | * 任务完成的回调. 50 | * @param task 51 | */ 52 | void taskComplete(Task task); 53 | /** 54 | * 任务最终失败的回调. 55 | * @param task 56 | * @param cause 失败原因 57 | */ 58 | void taskFailed(Task task, Throwable cause); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v1/TaskQueueDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v1; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 24 | 25 | import java.util.Random; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * 演示基于TSQ的任务队列的运行情况. 30 | */ 31 | public class TaskQueueDemoActivity extends AppCompatActivity { 32 | private TextView description; 33 | private TextView logTextView; 34 | 35 | private TaskQueue taskQueue; 36 | 37 | private static long taskIdCounter; 38 | private Random rand1 = new Random(); 39 | private Random rand2 = new Random(); 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_log_display); 45 | 46 | description = (TextView) findViewById(R.id.description); 47 | logTextView = (TextView) findViewById(R.id.log_display); 48 | 49 | description.setText(R.string.queueing_demo1_description); 50 | 51 | taskQueue = new TSQBasedTaskQueue(); 52 | taskQueue.setListener(new TaskQueue.TaskQueueListener() { 53 | @Override 54 | public void taskComplete(Task task) { 55 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") complete"); 56 | } 57 | 58 | @Override 59 | public void taskFailed(Task task, Throwable cause) { 60 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") failed, error message: " + cause.getMessage()); 61 | } 62 | }); 63 | 64 | /** 65 | * 启动5个任务 66 | */ 67 | for (int i = 0; i < 5; i++) { 68 | Task task = new Task() { 69 | private String taskId = String.valueOf(++taskIdCounter); 70 | 71 | @Override 72 | public void run() { 73 | //任务随机执行0~3秒 74 | try { 75 | TimeUnit.MILLISECONDS.sleep(rand1.nextInt(3000)); 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } 79 | 80 | //模拟失败情况: 以80%的概率失败 81 | if (rand2.nextInt(10) < 8) { 82 | throw new RuntimeException("runtime error..."); 83 | } 84 | } 85 | 86 | @Override 87 | public String getTaskId() { 88 | return taskId; 89 | } 90 | }; 91 | TextLogUtil.println(logTextView, "Add task (" + task.getTaskId() + ") to queue"); 92 | taskQueue.addTask(task); 93 | } 94 | 95 | } 96 | 97 | @Override 98 | protected void onDestroy() { 99 | taskQueue.setListener(null); 100 | taskQueue.destroy(); 101 | 102 | super.onDestroy(); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v2/CallbackBasedTaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v2; 18 | 19 | 20 | import android.util.Log; 21 | 22 | import java.util.LinkedList; 23 | import java.util.Queue; 24 | 25 | /** 26 | * Created by Tielei Zhang on 16/9/1. 27 | * 28 | * 基于Callback机制的队列实现 29 | */ 30 | public class CallbackBasedTaskQueue implements TaskQueue, Task.TaskListener { 31 | private static final String TAG = "TaskQueue"; 32 | 33 | /** 34 | * Task排队的队列. 不需要thread-safe 35 | */ 36 | private Queue taskQueue = new LinkedList(); 37 | 38 | private TaskQueueListener listener; 39 | private boolean stopped; 40 | 41 | /** 42 | * 一个任务最多重试次数. 43 | * 重试次数超过MAX_RETRIES, 任务则最终失败. 44 | */ 45 | private static final int MAX_RETRIES = 3; 46 | /** 47 | * 当前任务的执行次数记录(当尝试超过MAX_RETRIES时就最终失败) 48 | */ 49 | private int runCount; 50 | 51 | @Override 52 | public void addTask(Task task) { 53 | //新任务加入队列 54 | taskQueue.offer(task); 55 | task.setListener(this); 56 | 57 | if (taskQueue.size() == 1 && !stopped) { 58 | //当前是第一个排队任务, 立即执行它 59 | launchNextTask(); 60 | } 61 | } 62 | 63 | @Override 64 | public void setListener(TaskQueueListener listener) { 65 | this.listener = listener; 66 | } 67 | 68 | @Override 69 | public void destroy() { 70 | stopped = true; 71 | } 72 | 73 | private void launchNextTask() { 74 | //取当前队列头的任务, 但不出队列 75 | Task task = taskQueue.peek(); 76 | if (task == null) { 77 | //impossible case 78 | Log.e(TAG, "impossible: NO task in queue, unexpected!"); 79 | return; 80 | } 81 | 82 | Log.d(TAG, "start task (" + task.getTaskId() + ")"); 83 | task.start(); 84 | runCount = 1; 85 | } 86 | 87 | @Override 88 | public void taskComplete(Task task) { 89 | Log.d(TAG, "task (" + task.getTaskId() + ") complete"); 90 | finishTask(task, null); 91 | } 92 | 93 | @Override 94 | public void taskFailed(Task task, Throwable error) { 95 | if (runCount < MAX_RETRIES && !stopped) { 96 | //可以继续尝试 97 | Log.d(TAG, "task (" + task.getTaskId() + ") failed, try again. runCount: " + runCount); 98 | task.start(); 99 | runCount++; 100 | } 101 | else { 102 | //最终失败 103 | Log.d(TAG, "task (" + task.getTaskId() + ") failed, final failed! runCount: " + runCount); 104 | finishTask(task, error); 105 | } 106 | } 107 | 108 | /** 109 | * 一个任务最终结束(成功或最终失败)后的处理 110 | * @param task 111 | * @param error 112 | */ 113 | private void finishTask(Task task, Throwable error) { 114 | //回调 115 | if (listener != null && !stopped) { 116 | try { 117 | if (error == null) { 118 | listener.taskComplete(task); 119 | } 120 | else { 121 | listener.taskFailed(task, error); 122 | } 123 | } 124 | catch (Throwable e) { 125 | Log.e(TAG, "", e); 126 | } 127 | } 128 | task.setListener(null); 129 | 130 | //出队列 131 | taskQueue.poll(); 132 | 133 | //启动队列下一个任务 134 | if (taskQueue.size() > 0 && !stopped) { 135 | launchNextTask(); 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v2/Task.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v2; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/9/1. 21 | * 22 | * 供任务队列执行的异步任务接口定义. 23 | * 24 | * 第二个版本: 不再是同步任务, 变成异步任务. 25 | * 26 | */ 27 | public interface Task { 28 | /** 29 | * 唯一标识当前任务的ID 30 | * @return 31 | */ 32 | String getTaskId(); 33 | 34 | /** 35 | * 由于任务是异步任务, 那么start方法被调用只是启动任务; 36 | * 任务完成后会回调TaskListener. 37 | * 38 | * 注: start方法需在主线程上执行. 39 | */ 40 | void start(); 41 | 42 | /** 43 | * 设置回调监听. 44 | * @param listener 45 | */ 46 | void setListener(TaskListener listener); 47 | 48 | /** 49 | * 异步任务回调接口. 50 | */ 51 | interface TaskListener { 52 | /** 53 | * 当前任务完成的回调. 54 | * @param task 55 | */ 56 | void taskComplete(Task task); 57 | /** 58 | * 当前任务执行失败的回调. 59 | * @param task 60 | * @param cause 失败原因 61 | */ 62 | void taskFailed(Task task, Throwable cause); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v2/TaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v2; 18 | 19 | /** 20 | * Created by Tielei Zhang on 16/9/1. 21 | * 任务队列接口. 22 | * 23 | * 第二版: 对应第二版的Task接口. 24 | */ 25 | public interface TaskQueue { 26 | /** 27 | * 向队列中添加一个任务. 28 | * 29 | * @param task 30 | */ 31 | void addTask(Task task); 32 | 33 | /** 34 | * 设置监听器. 35 | * @param listener 36 | */ 37 | void setListener(TaskQueueListener listener); 38 | 39 | /** 40 | * 销毁队列. 41 | * 注: 队列在最后不用的时候, 应该主动销毁它. 42 | */ 43 | void destroy(); 44 | 45 | /** 46 | * 任务队列对外监听接口. 47 | */ 48 | interface TaskQueueListener { 49 | /** 50 | * 任务完成的回调. 51 | * @param task 52 | */ 53 | void taskComplete(Task task); 54 | /** 55 | * 任务最终失败的回调. 56 | * @param task 57 | * @param cause 失败原因 58 | */ 59 | void taskFailed(Task task, Throwable cause); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v2/TaskQueueDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v2; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 24 | import com.zhangtielei.demos.async.programming.queueing.v2.mock.MockAsyncTask; 25 | 26 | /** 27 | * 演示基于"异步+Callback"的任务队列的运行情况. 28 | */ 29 | public class TaskQueueDemoActivity extends AppCompatActivity { 30 | private TextView description; 31 | private TextView logTextView; 32 | 33 | private TaskQueue taskQueue; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_log_display); 39 | 40 | description = (TextView) findViewById(R.id.description); 41 | logTextView = (TextView) findViewById(R.id.log_display); 42 | 43 | description.setText(R.string.queueing_demo2_description); 44 | 45 | taskQueue = new CallbackBasedTaskQueue(); 46 | taskQueue.setListener(new TaskQueue.TaskQueueListener() { 47 | @Override 48 | public void taskComplete(Task task) { 49 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") complete"); 50 | } 51 | 52 | @Override 53 | public void taskFailed(Task task, Throwable cause) { 54 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") failed, error message: " + cause.getMessage()); 55 | } 56 | }); 57 | 58 | /** 59 | * 启动5个任务 60 | */ 61 | for (int i = 0; i < 5; i++) { 62 | Task task = new MockAsyncTask(); 63 | TextLogUtil.println(logTextView, "Add task (" + task.getTaskId() + ") to queue"); 64 | taskQueue.addTask(task); 65 | } 66 | 67 | } 68 | 69 | @Override 70 | protected void onDestroy() { 71 | taskQueue.setListener(null); 72 | taskQueue.destroy(); 73 | 74 | super.onDestroy(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v2/mock/MockAsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v2.mock; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import com.zhangtielei.demos.async.programming.queueing.v2.Task; 22 | 23 | import java.util.Random; 24 | import java.util.concurrent.ExecutorService; 25 | import java.util.concurrent.Executors; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * Created by Tielei Zhang on 16/9/1. 30 | * 31 | * 异步任务的一个假的实现. 32 | */ 33 | public class MockAsyncTask implements Task { 34 | private static long taskIdCounter; 35 | 36 | private String taskId; 37 | private TaskListener listener; 38 | 39 | private ExecutorService executorService = Executors.newCachedThreadPool(); 40 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 41 | private Random rand1 = new Random(); 42 | private Random rand2 = new Random(); 43 | 44 | public MockAsyncTask() { 45 | taskId = String.valueOf(++taskIdCounter); 46 | } 47 | 48 | @Override 49 | public String getTaskId() { 50 | return taskId; 51 | } 52 | 53 | @Override 54 | public void start() { 55 | executorService.execute(new Runnable() { 56 | @Override 57 | public void run() { 58 | //任务随机执行0~3秒 59 | try { 60 | TimeUnit.MILLISECONDS.sleep(rand1.nextInt(3000)); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } 64 | 65 | //模拟失败情况: 以80%的概率失败 66 | Exception error = null; 67 | if (rand2.nextInt(10) < 8) { 68 | error = new RuntimeException("runtime error..."); 69 | } 70 | 71 | final Exception finalError = error; 72 | mainHandler.post(new Runnable() { 73 | @Override 74 | public void run() { 75 | if (listener != null) { 76 | try { 77 | if (finalError == null) { 78 | listener.taskComplete(MockAsyncTask.this); 79 | } 80 | else { 81 | listener.taskFailed(MockAsyncTask.this, finalError); 82 | } 83 | } 84 | catch (Throwable e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | } 89 | }); 90 | 91 | } 92 | }); 93 | } 94 | 95 | @Override 96 | public void setListener(TaskListener listener) { 97 | this.listener = listener; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v3/RxJavaBasedTaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v3; 18 | 19 | import android.util.Log; 20 | import rx.Observable; 21 | import rx.Observer; 22 | import rx.Subscriber; 23 | import rx.observables.AsyncOnSubscribe; 24 | 25 | import java.util.LinkedList; 26 | import java.util.Queue; 27 | 28 | /** 29 | * Created by Tielei Zhang on 16/9/1. 30 | * 31 | * 基于RxJava机制的队列实现 32 | */ 33 | public class RxJavaBasedTaskQueue implements TaskQueue { 34 | private static final String TAG = "TaskQueue"; 35 | 36 | /** 37 | * Task排队的队列. 不需要thread-safe 38 | */ 39 | private Queue> taskQueue = new LinkedList>(); 40 | private boolean stopped; 41 | 42 | /** 43 | * 一个任务最多重试次数. 44 | * 重试次数超过MAX_RETRIES, 任务则最终失败. 45 | */ 46 | private static final int MAX_RETRIES = 3; 47 | 48 | @Override 49 | public Observable addTask(Task task) { 50 | //新任务加入队列 51 | final QueueElement element = new QueueElement(); 52 | element.task = task; 53 | //subscriber后面会赋值 54 | taskQueue.offer(element); 55 | 56 | //把异步任务task转成一个Observable, 用于当前addTask的返回 57 | Observable taskObservable = Observable.create(new AsyncOnSubscribe() { 58 | @Override 59 | protected Integer generateState() { 60 | return 0; 61 | } 62 | 63 | @Override 64 | protected Integer next(Integer state, long requested, Observer> observer) { 65 | Observable asyncObservable = Observable.create(new Observable.OnSubscribe() { 66 | @Override 67 | public void call(Subscriber subscriber) { 68 | //得到subscriber了,存入队列元素 69 | element.subscriber = subscriber; 70 | //这里能启动排队任务了 71 | if (taskQueue.size() == 1 && !stopped) { 72 | //当前是第一个排队任务, 立即执行它 73 | QueueElement element = taskQueue.peek(); 74 | launchNextTask(element); 75 | } 76 | } 77 | }); 78 | observer.onNext(asyncObservable); 79 | observer.onCompleted(); 80 | return 1; 81 | } 82 | }); 83 | 84 | return taskObservable; 85 | } 86 | 87 | @Override 88 | public void destroy() { 89 | stopped = true; 90 | } 91 | 92 | private void launchNextTask(final QueueElement element) { 93 | //取当前队列头的任务, 但不出队列 94 | if (element == null || element.task == null || element.subscriber == null) { 95 | //impossible case 96 | Log.e(TAG, "impossible: NO task element in queue, unexpected!"); 97 | return; 98 | } 99 | 100 | Task task = element.task; 101 | 102 | Log.d(TAG, "start task (" + task.getTaskId() + ")"); 103 | task.start().subscribe(new Subscriber() { 104 | @Override 105 | public void onNext(Object data) { 106 | element.taskResult = data; 107 | } 108 | 109 | @Override 110 | public void onCompleted() { 111 | taskComplete(element); 112 | } 113 | 114 | @Override 115 | public void onError(Throwable e) { 116 | taskFailed(element, e); 117 | 118 | } 119 | }); 120 | } 121 | 122 | public void taskComplete(QueueElement element) { 123 | element.runCount++; 124 | Log.d(TAG, "task (" + element.task.getTaskId() + ") complete"); 125 | finishTask(element, null); 126 | } 127 | 128 | public void taskFailed(QueueElement element, Throwable error) { 129 | element.runCount++; 130 | Task task = element.task; 131 | if (element.runCount < MAX_RETRIES && !stopped) { 132 | //可以继续尝试 133 | Log.d(TAG, "task (" + task.getTaskId() + ") failed, try again. runCount: " + element.runCount); 134 | launchNextTask(element); 135 | } 136 | else { 137 | //最终失败 138 | Log.d(TAG, "task (" + task.getTaskId() + ") failed, final failed! runCount: " + element.runCount); 139 | finishTask(element, error); 140 | } 141 | } 142 | 143 | /** 144 | * 一个任务最终结束(成功或最终失败)后的处理 145 | * @param element 146 | * @param error 147 | */ 148 | private void finishTask(QueueElement element, Throwable error) { 149 | //回调 150 | 151 | if (error == null) { 152 | if (element.taskResult != null) { 153 | element.subscriber.onNext(element.taskResult); 154 | } 155 | element.subscriber.onCompleted(); 156 | } 157 | else { 158 | element.subscriber.onError(error); 159 | } 160 | 161 | //出队列 162 | taskQueue.poll(); 163 | 164 | //启动队列下一个任务 165 | if (taskQueue.size() > 0 && !stopped) { 166 | QueueElement nextElement = taskQueue.peek(); 167 | launchNextTask(nextElement); 168 | } 169 | } 170 | 171 | /** 172 | * 任务队列里存放的对象类型. 173 | */ 174 | private static class QueueElement { 175 | /** 176 | * 要执行的任务. 177 | */ 178 | public Task task; 179 | /** 180 | * 用于执行完任务回调的Subscriber. 181 | */ 182 | public Subscriber subscriber; 183 | /** 184 | * 任务已经执行的次数, 失败超过MAX_RETRIES次就算失败 185 | */ 186 | public int runCount; 187 | /** 188 | * 可能的任务执行结果. 189 | * 如果任务没有返回结果, 那么这个值为null 190 | */ 191 | public R taskResult; 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v3/Task.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v3; 18 | 19 | import rx.Observable; 20 | 21 | /** 22 | * Created by Tielei Zhang on 16/9/1. 23 | * 24 | * 供任务队列执行的异步任务接口定义. 25 | * 26 | * 第三个版本: 不再使用TaskListener传递回调, 而是使用Observable. 27 | * 28 | * @param 异步任务执行完要返回的数据类型. 29 | */ 30 | public interface Task { 31 | /** 32 | * 唯一标识当前任务的ID 33 | * @return 34 | */ 35 | String getTaskId(); 36 | 37 | /** 38 | * 39 | * 启动任务. 40 | * 41 | * 注: start方法需在主线程上执行. 42 | * 43 | * @return 一个Observable. 调用者通过这个Observable获取异步任务执行结果. 44 | */ 45 | Observable start(); 46 | } 47 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v3/TaskQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v3; 18 | 19 | import rx.Observable; 20 | 21 | /** 22 | * Created by Tielei Zhang on 16/9/1. 23 | * 24 | * 任务队列接口. 25 | * 26 | * 第三版: 对应第三版的Task接口, 并且舍弃TaskQueueListener. 27 | */ 28 | public interface TaskQueue { 29 | /** 30 | * 向队列中添加一个任务. 31 | * 32 | * @param task 33 | * @param 异步任务执行完要返回的数据类型. 34 | * @return 一个Observable. 调用者通过这个Observable获取异步任务执行结果. 35 | */ 36 | Observable addTask(Task task); 37 | 38 | /** 39 | * 销毁队列. 40 | * 注: 队列在最后不用的时候, 应该主动销毁它. 41 | */ 42 | void destroy(); 43 | } 44 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v3/TaskQueueDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v3; 18 | 19 | import android.os.Bundle; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.widget.TextView; 22 | import com.zhangtielei.demos.async.programming.R; 23 | import com.zhangtielei.demos.async.programming.common.utils.TextLogUtil; 24 | import com.zhangtielei.demos.async.programming.queueing.v3.mock.MockAsyncTask; 25 | import rx.Observable; 26 | import rx.Subscriber; 27 | import rx.Subscription; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | /** 33 | * 演示基于RxJava的任务队列的运行情况. 34 | */ 35 | public class TaskQueueDemoActivity extends AppCompatActivity { 36 | private TextView description; 37 | private TextView logTextView; 38 | 39 | private TaskQueue taskQueue; 40 | 41 | private List subscriptionList = new ArrayList(); 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_log_display); 47 | 48 | description = (TextView) findViewById(R.id.description); 49 | logTextView = (TextView) findViewById(R.id.log_display); 50 | 51 | description.setText(R.string.queueing_demo3_description); 52 | 53 | taskQueue = new RxJavaBasedTaskQueue(); 54 | /** 55 | * 启动5个任务 56 | */ 57 | for (int i = 0; i < 5; i++) { 58 | final MockAsyncTask task = new MockAsyncTask(); 59 | TextLogUtil.println(logTextView, "Add task (" + task.getTaskId() + ") to queue"); 60 | Observable observable = taskQueue.addTask(task); 61 | Subscription subscription = observable.subscribe(new Subscriber() { 62 | @Override 63 | public void onCompleted() { 64 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") complete"); 65 | } 66 | 67 | @Override 68 | public void onError(Throwable e) { 69 | TextLogUtil.println(logTextView, "Task (" + task.getTaskId() + ") failed, error message: " + e.getMessage()); 70 | } 71 | 72 | @Override 73 | public void onNext(Void aVoid) { 74 | 75 | } 76 | }); 77 | 78 | subscriptionList.add(subscription); 79 | } 80 | 81 | } 82 | 83 | @Override 84 | protected void onDestroy() { 85 | for (Subscription subscription : subscriptionList) { 86 | subscription.unsubscribe(); 87 | } 88 | taskQueue.destroy(); 89 | 90 | super.onDestroy(); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/java/com/zhangtielei/demos/async/programming/queueing/v3/mock/MockAsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Tielei Zhang (zhangtielei.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.zhangtielei.demos.async.programming.queueing.v3.mock; 18 | 19 | import com.zhangtielei.demos.async.programming.queueing.v3.Task; 20 | import rx.Observable; 21 | import rx.Subscriber; 22 | import rx.android.schedulers.AndroidSchedulers; 23 | import rx.schedulers.Schedulers; 24 | 25 | import java.util.Random; 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * Created by Tielei Zhang on 16/9/1. 32 | * 33 | * 异步任务的一个假的实现. 34 | */ 35 | public class MockAsyncTask implements Task { 36 | private static long taskIdCounter; 37 | 38 | private String taskId; 39 | 40 | private ExecutorService executorService = Executors.newCachedThreadPool(); 41 | private Random rand1 = new Random(); 42 | private Random rand2 = new Random(); 43 | 44 | public MockAsyncTask() { 45 | taskId = String.valueOf(++taskIdCounter); 46 | } 47 | 48 | @Override 49 | public String getTaskId() { 50 | return taskId; 51 | } 52 | 53 | @Override 54 | public Observable start() { 55 | return Observable.create(new Observable.OnSubscribe() { 56 | @Override 57 | public void call(Subscriber subscriber) { 58 | //任务随机执行0~3秒 59 | try { 60 | TimeUnit.MILLISECONDS.sleep(rand1.nextInt(3000)); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } 64 | 65 | //模拟失败情况: 以80%的概率失败 66 | Exception error = null; 67 | if (rand2.nextInt(10) < 8) { 68 | error = new RuntimeException("runtime error..."); 69 | } 70 | 71 | if (error == null) { 72 | //当前异步任务没有返回数据, 不用调用onNext 73 | subscriber.onCompleted(); 74 | } 75 | else { 76 | subscriber.onError(error); 77 | } 78 | 79 | } 80 | }).subscribeOn(Schedulers.from(executorService)) 81 | .observeOn(AndroidSchedulers.mainThread()); 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/layout/activity_item_list.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | 34 | 35 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/layout/activity_log_display.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | 35 | 36 | 41 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tielei/AsyncProgrammingDemos/87a7ec522f0e37520995e8e31c490b477dd1dae1/AndroidDemos/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tielei/AsyncProgrammingDemos/87a7ec522f0e37520995e8e31c490b477dd1dae1/AndroidDemos/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tielei/AsyncProgrammingDemos/87a7ec522f0e37520995e8e31c490b477dd1dae1/AndroidDemos/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tielei/AsyncProgrammingDemos/87a7ec522f0e37520995e8e31c490b477dd1dae1/AndroidDemos/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 21 | 64dp 22 | 23 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 16dp 20 | 16dp 21 | 22 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | AsyncProgramming 19 | 20 | (一)异步处理概述举例 21 | (二)异步任务的回调 22 | (三)多个异步任务协作 23 | (四)异步任务和队列 24 | (五)异步任务的取消和暂停,以及start ID(未完成) 25 | (六)关于封屏与不封屏(未完成) 26 | (七)Android Service实例分析(未完成) 27 | 28 | (1)演示通过全局保存一份上下文来实现表情包下载 29 | (2)演示通过映射关系保存上下文来实现表情包下载 30 | (3)演示采用为每一个异步任务创建一个接口实例的方式来实现表情包下载 31 | (4)演示利用支持上下文传递的异步接口来实现表情包下载 32 | 33 | (1)通过一个静态图片多级缓存的例子,演示"多个异步任务先后接续执行" 34 | (2)通过一个同时发起多个网络请求的例子,演示"多个异步任务并发执行,结果合并" 35 | (3)通过一个页面缓存的例子,演示"多个异步任务并发执行,一方优先" 36 | (4)演示使用RxJava zip来实现并发网络请求 37 | 38 | (1)演示TSQ实现的任务队列 39 | (2)演示通过普通的"异步+Callback"方式实现的任务队列 40 | (3)演示使用RxJava实现的任务队列 41 | 42 | 本页面用于演示绑定和解绑一个Service的异步过程 43 | 本页面通过实现一个表情包下载器,来演示全局保存一份上下文的技术 44 | 本页面通过实现一个表情包下载器,来演示用映射关系来保存上下文的技术 45 | 本页面通过实现一个表情包下载器,来演示为每一个异步任务创建一个接口实例来传递上下文的技术 46 | 本页面通过实现一个表情包下载器,来演示利用一个支持上下文传递的异步接口的上下文传递方式(推荐方式) 47 | 本页面通过调用一个带有多级缓存的图片下载器,来演示"多个异步任务先后接续执行"的多任务协作关系 48 | 本页面通过同时发起多个网络请求的例子,来演示"多个异步任务并发执行,全部完成"的多任务协作关系(注意: 本实例多次执行会产生不同的执行时序) 49 | 本页面通过一个页面缓存的例子,来演示"多个异步任务并发执行,优先完成"的多任务协作关系(注意: 本实例多次执行会产生不同的执行时序) 50 | 本页面用于演示如何使用RxJava的zip操作来处理并发的两个请求(注意: 本实例多次执行会产生不同的执行时序) 51 | 52 | 本页面演示使用TSQ来实现任务队列(这种方式在客户端编程中很少见) 53 | 本页面演示使用"异步+Callback"方式实现任务队列 54 | 本页面演示使用RxJava实现任务队列 55 | 56 | 57 | MultiRequestsDemoActivity 58 | PageCachingDemoActivity 59 | ServiceBindingDemoActivity 60 | EmojiDownloadDemoListActivity 61 | EmojiDownloadDemoActivity 62 | MultiTaskDemoListActivity 63 | ImageLoaderDemoActivity 64 | TaskQueueDemoActivity 65 | 66 | Hello world! 67 | Settings 68 | QueueingDemoListActivity 69 | 70 | -------------------------------------------------------------------------------- /AndroidDemos/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AndroidDemos/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.0.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AndroidDemos/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /AndroidDemos/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tielei/AsyncProgrammingDemos/87a7ec522f0e37520995e8e31c490b477dd1dae1/AndroidDemos/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /AndroidDemos/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /AndroidDemos/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /AndroidDemos/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /AndroidDemos/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsyncProgrammingDemos 2 | Demo code for Tielei's Blog about async programming in Android & iOS. 3 | 4 | [《Android和iOS开发中的异步处理》](http://zhangtielei.com/posts/blog-series-async-task-1.html) 5 | 6 | License 7 | ======= 8 | 9 | Copyright 2016 Tielei Zhang (zhangtielei.com). 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. -------------------------------------------------------------------------------- /iOSDemos/iOSDemos.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // iOSDemos 4 | // 5 | // Created by Charles Zhang on 16/8/16. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // iOSDemos 4 | // 5 | // Created by Charles Zhang on 16/8/16. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ReachabiityTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReachabiityTestViewController.h 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 3/27/16. 6 | // Copyright © 2016 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ReachabiityTestViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ReachabiityTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MyViewController.m 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 3/27/16. 6 | // Copyright © 2016 tielei. All rights reserved. 7 | // 8 | 9 | #import "ReachabiityTestViewController.h" 10 | #import "ServerConnection.h" 11 | 12 | @interface ReachabiityTestViewController () { 13 | ServerConnection *serverConnection; 14 | } 15 | 16 | @end 17 | 18 | @implementation ReachabiityTestViewController 19 | 20 | 21 | - (void)awakeFromNib { 22 | [super awakeFromNib]; 23 | 24 | serverConnection = [[ServerConnection alloc] init]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/Reachability.h: -------------------------------------------------------------------------------- 1 | // 2 | // Reachability.h 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 16/3/24. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | 13 | extern NSString *const networkStatusNotificationInfoKey; 14 | 15 | extern NSString *const kReachabilityChangedNotification; 16 | 17 | 18 | typedef NS_ENUM(uint32_t, NetworkStatus) { 19 | NotReachable = 0, 20 | ReachableViaWiFi = 1, 21 | ReachableViaWWAN = 2 22 | }; 23 | 24 | @interface Reachability : NSObject { 25 | @private 26 | SCNetworkReachabilityRef reachabilityRef; 27 | } 28 | 29 | /** 30 | * 开始网络状态监听 31 | */ 32 | - (BOOL)startNetworkMonitoring; 33 | /** 34 | * 结束网络状态监听 35 | */ 36 | - (BOOL)stopNetworkMonitoring; 37 | /** 38 | * 同步获取当前网络状态 39 | */ 40 | - (NetworkStatus) currentNetworkStatus; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/Reachability.m: -------------------------------------------------------------------------------- 1 | // 2 | // Reachability.m 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 16/3/24. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import "Reachability.h" 10 | 11 | #import 12 | #import 13 | 14 | NSString *const networkStatusNotificationInfoKey = @"networkStatus"; 15 | 16 | NSString *const kReachabilityChangedNotification = @"NetworkReachabilityChangedNotification"; 17 | 18 | @implementation Reachability 19 | 20 | 21 | - (instancetype)init { 22 | self = [super init]; 23 | if (self) { 24 | struct sockaddr_in zeroAddress; 25 | // bzero(&zeroAddress, sizeof(zeroAddress)); 26 | memset(&zeroAddress, 0, sizeof(zeroAddress)); 27 | zeroAddress.sin_len = sizeof(zeroAddress); 28 | zeroAddress.sin_family = AF_INET; 29 | 30 | reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress); 31 | 32 | } 33 | 34 | return self; 35 | } 36 | 37 | - (void)dealloc { 38 | if (reachabilityRef) { 39 | CFRelease(reachabilityRef); 40 | } 41 | } 42 | 43 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { 44 | 45 | Reachability *reachability = (__bridge Reachability *) info; 46 | 47 | @autoreleasepool { 48 | NetworkStatus networkStatus = [reachability currentNetworkStatus]; 49 | [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:reachability userInfo:@{networkStatusNotificationInfoKey : @(networkStatus)}]; 50 | } 51 | } 52 | 53 | - (BOOL)startNetworkMonitoring { 54 | SCNetworkReachabilityContext context = {0, (__bridge void * _Nullable)(self), NULL, NULL, NULL}; 55 | 56 | if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) { 57 | if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { 58 | return YES; 59 | } 60 | 61 | } 62 | 63 | return NO; 64 | } 65 | 66 | - (BOOL)stopNetworkMonitoring { 67 | return SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 68 | } 69 | 70 | - (NetworkStatus) currentNetworkStatus { 71 | //TODO: 72 | return ReachableViaWiFi; 73 | } 74 | 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ServerConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // ServerConnection.h 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 3/27/16. 6 | // Copyright © 2016 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ServerConnection : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ServerConnection.m: -------------------------------------------------------------------------------- 1 | // 2 | // ServerConnection.m 3 | // MyBlogAsyncTaskSeriesDemos 4 | // 5 | // Created by Charles Zhang on 3/27/16. 6 | // Copyright © 2016 tielei. All rights reserved. 7 | // 8 | 9 | #import "ServerConnection.h" 10 | #import "Reachability.h" 11 | 12 | @interface ServerConnection() { 13 | //用户执行socket操作的GCD queue 14 | dispatch_queue_t socketQueue; 15 | Reachability *reachability; 16 | } 17 | @end 18 | 19 | @implementation ServerConnection 20 | 21 | - (instancetype)init { 22 | self = [super init]; 23 | if (self) { 24 | socketQueue = dispatch_queue_create("SocketQueue", NULL); 25 | 26 | reachability = [[Reachability alloc] init]; 27 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStateChanged:) name:kReachabilityChangedNotification object:reachability]; 28 | [reachability startNetworkMonitoring]; 29 | NSLog(@"ServerConnection init, start network monitoring..."); 30 | } 31 | return self; 32 | } 33 | 34 | - (void)dealloc { 35 | [reachability stopNetworkMonitoring]; 36 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 37 | NSLog(@"ServerConnection dealloc, stop network monitoring... isMainThread? %d", (dispatch_get_current_queue() == dispatch_get_main_queue())); 38 | } 39 | 40 | 41 | - (void)networkStateChanged:(NSNotification *)notification { 42 | NetworkStatus networkStatus = [notification.userInfo[networkStatusNotificationInfoKey] unsignedIntValue]; 43 | if (networkStatus != NotReachable) { 44 | //网络变化,重连 45 | NSLog(@"networkStateChanged detected"); 46 | dispatch_async(socketQueue, ^{ 47 | [self reconnect]; 48 | }); 49 | } 50 | } 51 | 52 | 53 | - (void)reconnect { 54 | //TODO: 55 | NSLog(@"reconnecting... isMainThread? %d", (dispatch_get_current_queue() == dispatch_get_main_queue())); 56 | sleep(5); 57 | } 58 | 59 | 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // iOSDemos 4 | // 5 | // Created by Charles Zhang on 16/8/16. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // iOSDemos 4 | // 5 | // Created by Charles Zhang on 16/8/16. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /iOSDemos/iOSDemos/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iOSDemos 4 | // 5 | // Created by Charles Zhang on 16/8/16. 6 | // Copyright © 2016年 tielei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------