程序员徐公

微信公众号:【徐公】

0%

前言

IntentService使用及源码分析

HandlerThread源码分析

AsyncTask使用及封装实践

AsyncTask源码分析

这篇博客主要是讲解AsyncTask的使用及封装实践,对于新手们还是有很大的参考意义的,尤其是AsyncTask的封装实践这部分。对于老鸟们,你们可以跳过了。同时需要声明的一点是,下面下载的例子只是进行简单的下载而已,并没有支持断点续传下载。需要的话请自行到github上面找相应的库,因为这并不是本篇博客的重点。

这篇博客主要讲解以下问题:

  • AsyncTask的简单使用
  • AsyncTask的封装使用
  • AsyncTask使用注意事项

AsyncTask的使用例子

简介

AsyncTask ,异步任务。没错,就想字面上理解的那样。它允许我们在子线程执行耗时任务,在UI 线程更新操作(如更新进度条等)。简单来说,就是帮我们做好了子线程与UI 线程的通讯,我们只需要调用响应的方法实现即可。底层是用Handler消息机制实现的。

在Android开发中,我们经常需要下载各种东西,为了给用户较好的体验,我们经常需要显示下载进度。今天我们用以这个为例子,来教大家怎样使用AsyncTak。当然,github上面有很多开源库,实现断点下载,文件重命名等。不过这些不是本篇博客的重点。

效果图

AsyncTask的主要几个方法

  • Void onPreExecute()

在task 任务开始执行的时候调用,在doInBackground(Params… params)方法之前调用,在主线程中执行

  • Result doInBackground(Params… params)

主要用来执行耗时操作,在子线程中执行,Params为我们参数的类型。而Result这个泛型,是我们返回的类型(可以是Integer,Long,String等等类型,只要不是八种基本类型就OK),同时 Result 的类型将作为 onPostExecute(Result result)的参数。

  • Void onProgressUpdate(Progress… values)

Runs on the UI thread after publishProgress(Progress…) is invoked. 当我们调用 publishProgress()方法的时候,会调用 onProgressUpdate()这个方法

  • Void onPostExecute(Result result)
    在doInBackground()方法执行完毕之后,会调用这个方法,是在主线程中执行的。但如果我们手动调用了cancelled()方法,那么这个方法将不会被调用。

  • void onCancelled()

在Task 任务取消的时候会调用

  • execute(Params… params)

Executes the task with the specified parameters.当我们调用这个方法的时候,会执行任务

  • executeOnExecutor(Executor exec, Params… params)

在指定的线程池里面执行Task

需要注意的是,Params,Progress,Result 并不是一种特定的类型,它其实是泛型,它支持除了八种基本类型之外的类型,跟普通的泛型一样。

AsyncTask使用的几个步骤

这里我们以下载一个apk为例讲解

  1. 写一个类继承AsyncTask,并传入Params,Progress,Result 。三个参数的类型。

比如我们传入的 Params,Progress,Result 的参数的类型分别为 Void, FileInfo, FileInfo,那我们可以这样写。

1
2
3
private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo>{

}

那Void, FileInfo, FileInfo,这几个参数的类型在哪里体现出来呢?

请看下面注释

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
private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo> {

---

// 方法参数的类型为Void,跟我们传入的Void一致,返回类型为 FileInfo ,跟我们传入Result的类型FileInfo一致
@Override
protected FileInfo doInBackground(Void... params) {

}

// 方法参数类型为FileInfo,跟我们传入Progress的类型FileInfo一致
@Override
protected void onProgressUpdate(FileInfo... values) {

}


// 方法参数FileInfo,跟我们传入Result的类型FileInfo一致
@Override
protected void onPostExecute(FileInfo fileInfo) {


}


}
  1. 如果我们更新进度的话,需要重写 onProgressUpdate()方法,并在doInBackground()方法里面调用publishProgress()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected FileInfo doInBackground(Void... params) {

publishProgress(fileInfo);


}

@Override
protected void onProgressUpdate(FileInfo... values) {
super.onProgressUpdate(values);
refreshProgress(values[0]);
}


  1. 当我们调用execute(Params… params) 或者 executeOnExecutor(Executor exec, Params… params) 方法的时候,Task将被防盗相应的 Executor 执行。
1
2
MyDownloadTask myDownloadTask = new MyDownloadTask(mDownloadUrl, mDstPath);
myDownloadTask.execute();

完整的Task代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo> {

String mDownLoadUrl;
String mDstPath;

public MyDownloadTask(String downloadUrl, String dstPath) {
this.mDownLoadUrl = downloadUrl;
this.mDstPath = dstPath;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
start();
}

@Override
protected FileInfo doInBackground(Void... params) {
//url字符串,检查网址是否已http:// 开头
mDownLoadUrl = (mDownLoadUrl.startsWith("http://")) ? mDownLoadUrl : "http://" +
mDownLoadUrl;
Log.d(TAG, "doInBackground: mDownLoadUrl=" + mDownLoadUrl);
Log.d(TAG, "doInBackground: mDstPath=" + mDstPath);
URL url = null;
FileInfo fileInfo = null;
int contentLength = -1;
int downloadLength = 0;
OutputStream output = null;
InputStream istream = null;
try {
url = new URL(mDownLoadUrl);
//打开到url的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
contentLength = connection.getContentLength();
Log.i(TAG, "doInBackground: contentLength=" + contentLength);
//O部分,大体来说就是先检查文件夹是否存在,不存在则创建
istream = connection.getInputStream();
String filename = mDownLoadUrl.substring(mDownLoadUrl.lastIndexOf("/") + 1);

File dir = new File(mDstPath);
if (!dir.exists()) {
dir.mkdir();
}
File file = new File(mDstPath + filename);
// 如果存在同名文件,重命名
if (file.exists()) {
file = FileUtils.rename(file.getPath());
}


output = new FileOutputStream(file);
byte[] buffer = new byte[1024 * 4];
int count = 0;
int len = -1;
while ((len = istream.read(buffer)) != -1) {
output.write(buffer, 0, len);
downloadLength += len;

if (count == 10) {
fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath
(), file.getName());
publishProgress(fileInfo);
count = 0;
}
count++;

}
// 有可能count还没有走到10
fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath(), file
.getName());
publishProgress(fileInfo);
output.flush();
output.close();
istream.close();

} catch (Exception e) {
e.printStackTrace();
try {
IOUtils.close(output);
IOUtils.close(istream);
} catch (IOException e1) {
e1.printStackTrace();
}

} finally {
try {
IOUtils.close(output);
IOUtils.close(istream);
} catch (IOException e1) {
e1.printStackTrace();
}
}
return fileInfo;
}

@Override
protected void onProgressUpdate(FileInfo... values) {
super.onProgressUpdate(values);
refreshProgress(values[0]);
}

@Override
protected void onPostExecute(FileInfo fileInfo) {
super.onPostExecute(fileInfo);
downloadfinish(fileInfo);

}

@Override
protected void onCancelled() {
super.onCancelled();
}
}

private void start() {
mTvDownloadText.setText("开始下载");
mProgressBar.setMax(100);
mProgressBar.setProgress(0);
}

private void downloadfinish(FileInfo fileInfo) {
Log.i(TAG, "onPostExecute: 下载完成=" + fileInfo.mPath);
Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
}

private void refreshProgress(FileInfo value) {
FileInfo fileInfo = value;
if (fileInfo != null) {
mProgressBar.setMax((int) fileInfo.mLength);
mProgressBar.setProgress((int) fileInfo.mDownloadLength);
mDownText = fileInfo.mFile.getName() + "下载了" + fileInfo.mDownloadLength + "总长度是" +
fileInfo.mLength;
mTvDownloadText.setText(mDownText);
}
}



AsyncTask的封装使用

前面我们讲完了AsyncTask的基本使用,不知道你有没有发现,其实代码耦合性是挺高的,

  • 我们直接在 onProgressUpdata(),onPostExecute()方法里面更新我们的界面,即我们的AsyncTask访问了我们Activity里面的控件,那如果我们修改了Activity的控件,我们岂不是又要去阅读AsyncTask的代码,去做相应的修改。
  • 下一次我们如果要下载别的东西,按照我们前面的代码,我们又要重新复制一份,这样无疑是做了很多重复的工作。

说到这样,我相信大多数人的第一感觉就是把AsyncTask提取为外部类,封装起来。是的,确实,我们就是要把AsyncTask提取为外部类。那提取为歪不累之后呢?我们要访问Activity里面的空间,要怎样访问呢?

  1. 在Activity里面定义静态方法
  2. 把需要访问的View对象通过构造函数传递进来
  3. 采用接口回调机制

前面说到的三种方法,是可以做到AsyncTask与外界进行通讯的。但第一第二中方法明显不行。原因如下:

  • 第一种方法定义静态方法,那View对象也必须定义为static变量,这static变量的级别比较高,不易被垃圾回收机制回收,易发生没存泄露。
  • 第二种方法,把需要访问的View对象通过构造函数传递进来。如果需要访问的对象少的话,勉强可以接受,如果多的话,那岂不是要定义很多成员变量。不过最致命的还算是代码耦合性太高了。还不如AsyncTask直接作为内部类。

好了,说了这么多,下面我们一起来看怎样使用接口回调机制来进行解耦。

AsyncTask 使用接口回调机制来进行解耦

  1. 使用接口回调机制,首先我们必须有一个接口
1
2
3
4
5
6
7
8
public interface DownloadListener {

void onStart();
void onProgress(FileInfo fileInfo);
void onFinish(FileInfo FileInfo);
void onPaused(FileInfo fileInfo);
void onCancled();
}
  1. 将DownLoadTask提取为一个外部类,并将需要传递的参数传递进来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DownloadTask extends AsyncTask<Void,FileInfo,FileInfo> {

private String mDownloadUrl;
private final String mDstPath;
private final String mFileName;
private final DownloadListener mDownloadListener;

public DownloadTask(String downloadUrl, String dstPath, String fileName, DownloadListener downloadListener){
mDownloadUrl = downloadUrl;
mDstPath = dstPath;
mFileName = fileName;
mDownloadListener = downloadListener;
}
}
  1. 在相应的地方调用我们接口的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class DownloadTask extends AsyncTask<Void,FileInfo,FileInfo> {

----

@Override
protected void onPreExecute() {
super.onPreExecute();
mDownloadListener.onStart();
}

@Override
protected FileInfo doInBackground(Void... params) {

----
int len = -1;
while ((len = istream.read(buffer)) != -1) {
output.write(buffer, 0, len);
downloadLength += len;

if (count == 10) {
fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath
(), file.getName());
publishProgress(fileInfo);
count = 0;
}
count++;

}
// 有可能count还没有走到10
fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath(), file
.getName());
publishProgress(fileInfo);
output.flush();
output.close();
istream.close();



return fileInfo;
}

@Override
protected void onProgressUpdate(FileInfo... values) {
super.onProgressUpdate(values);
mDownloadListener.onProgress(values[0]);
}

@Override
protected void onPostExecute(FileInfo fileInfo) {
super.onPostExecute(fileInfo);
mDownloadListener.onFinish(fileInfo);
}

@Override
protected void onCancelled() {
super.onCancelled();
mDownloadListener.onCancled();
}
}

使用

以后我们要下载东西,只需要调用下面的方法即可。同时,如果产品再更改需求,比如,从显示一个进度条ProgressDialog对话框,改成显示一个ProgressBar,我们只需要在
onProgress()里面做相应的修改就好了,在也不用去阅读DownloadTask里面的代码呢?减少了代码的耦合性,是不是瞬间感觉世界很美好呢?

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
mDownloadTask = new DownloadTask(mDownloadUrl, mDstPath, null, new
DownloadListener() {
@Override
public void onStart() {
start();
}

@Override
public void onProgress(FileInfo fileInfo) {
refreshProgress(fileInfo);
}

@Override
public void onFinish(FileInfo fileInfo) {
downloadfinish(fileInfo);
}

@Override
public void onPaused(FileInfo fileInfo) {

}

@Override
public void onCancled() {

}
});
mDownloadTask.execute();

AsyncTask使用的注意事项

  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
  • The task instance must be created on the UI thread.(AsyncTask必须在UI 线程里面初始化
  • execute(Params…) must be invoked on the UI thread.
  • Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) manually.(不要手动地调用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 这些方法)
  • The task can be executed only once (an exception will be thrown if a second execution is attempted.) (Task任务只能被执行一次,否则会抛出异常)

相关知识点推荐:

IntentService使用及源码分析

HandlerThread源码分析

AsyncTask使用及封装实践

AsyncTask源码分析

Demo下载地址

光阴似箭,日月如梭,时间真的过得飞快。
加上实习,从事 Android 开发,差不多有 5 年了,在这里,我分享一下我的经验,在 Android 的学习路上,我是站在巨人的肩膀上成长起来的。

大概分为三个部分

一、Android 职业规划
二、Android 学习路线
三、如何进入大厂
三、Android 学习资料分享

Android 成长生涯

1-3 年

前 3 年,我觉得是我们成长最快的时候。

尤其是刚开始工作的一年,刚开始接触项目,我们会接触到很多新知识,比较网络框架的封装,Android 架构 MVVN,MVC,MVP 等。

这段时期是我们进步最快的时候,因为很多东西我们都不会,我们会逼着自己去学习。

第二到第三年,也是我们成长的关键时期。这时候,在项目中,我们基本会自己独立负责需求。这时候的我们,大多数基本功能都可以自己独立实现。需要注意的是代码风格和代码思维的培养

代码风格,指的是编码习惯。比如方法命名,方法的合理拆分,适当添加注解,注释。听起来可能会很虚,但是千万别小看这些细节。反正我在组里见过一些工作五六年的,代码一坨一坨的,像 si 一样。

代码思维,有点类似于人的视野和思考方式吧。很多时候,实现某个效果,有很多种方案。我们为了应付需求,可能会随便选择一种,没有去了解其他方案。而这恰恰错过了我们成长的最佳时期。

记住,永远别给自己设限,或许在编程的路上会遇到很多问题,但是总会有解决方法的,而这取决你的态度。

3-5 年

这个时期,不出意外,我们基本都是高级工程师了。这时候大多数人会遇到瓶颈,不知道学些什么好。好像什么都会,但是不够深入。我也遇到这样的情况,现在正在深挖。

这方面我暂时也给不了太多意见,只能说,选择一个方向,静下心来,去学习,相信能收获到我们想要的。

学习方向有很多,比如 Android 性能优化(启动,卡顿, Crash 治理), JNI C++,gradle 打包,编译速度,音视频等等,这些都是大的方向。

最近写的一些文章,自我感觉还行,有兴趣的可以看看。

Android 启动优化(一) - 有向无环图

Android 启动优化(二) - 拓扑排序的原理以及解题思路

Android 启动优化(三)- AnchorTask 开源了

Android 启动优化(四)- AnchorTask 是怎么实现的

Android 启动优化(五)- AnchorTask 1.0.0 版本正式发布了

Android 启动优化(六)- 深入理解布局优化

5 年之后

技术人的四大出路,你适合哪一个

大多数技术人,一般会有以下方向。

  • 第一:成为技术大神,比如架构师,及技术专家等。
  • 第二:技术管理,比如技术总监等。
  • 第三:技术转型,如转项目管理,产品等,你牛逼的话,说不定下一个张小龙就是你(哥们,想多了,那有这么容易)。
  • 第四:自主创业,做自媒体等

我目前来说,可能会选择第一条路吧,成为一名技术专家。

在技术的路上,或许会很累,很难,可那又怎样呢

35 岁中年职业危机,很多职业都有,不仅仅局限于程序员。

从本质上面说,自身竞争力下降占很大原因。毕竟,优秀的人才从来不缺少机会。

既然选择了,便只顾风雨兼程。

为了未来有更多的选择,从现在开始,朝着自己的目标努力奋斗吧,书写属于自己的人生。


Android 学习路线

下面,说一下 Android 的学习路线,不会很具体,主要是从大的方面来说,感兴趣的可以看一下。

搭建基本的开发环境,敲下 Hello world

首先你配置好电脑开发环境。首先需要配置 Java,AndroidStudio 的开发环境。

现在的 IDE 对比几年前的,真的是方便多了,可以自动帮我们配置好环境变量。还记得当年刚开始配环境的时候,折腾了两三天。在配置变量的时候,有时候英文的逗号输成中文的,在 cmd 窗口输入 Java,一直提醒没有这个命令,一把鼻涕一行泪

依稀还记得,当配置完环境之后,敲下第一段程序 Hello world 时的那份喜悦。

掌握基本的语法和常用的数据结构

第一:你要先掌握基本的语法,可以从一些书籍开始学起。跟着书本慢慢敲,从第一个程序 Hello World 慢慢敲起,万丈高楼平地起

由于 Android 是用 java 或者 kotlin 语言开发的,建议优先掌握 java 语言。这里推荐两本书籍。

  1. java 编程思想,一本很经典的 java 书籍
  2. java 剑指 offer,面试神器。校招的时候,有很多面试题目,里面都有涉及到。

第二:掌握基本的语法之后,常用的数据结构,数组,链表,队列,栈。这些基本的概念和语法要知道。还有线程,文件 IO 操作,网络操作。

学完这些之后,Java 的基本知识你大概掌握了,这时候建议你动手写一些小项目或者 Demo,比如坦克大战,计算器等,这些网上都有代码。想跟着视频一起学的话,推荐马士兵的视频,可以在我的微信公众号 程序员徐公 回复”Java“,会将教程发给你

Android 学习入门

掌握这些之后,你可以去看一些 Android 入门的书籍或者一些视频。

书籍我推荐郭霖的第一行代码,目前出了第三版了,写得真不错。

视频的话,我推荐你可以看黑马的 Android 视频,里面会从一些基本语法讲起,接着是项目实战,会手把手教你实现简单的新闻客户端,360 手机卫士等等。

想获取黑马视频资料的话,可以去我的微信公众号程序员徐公回复”黑马“两字,会将教程发给你。

Android 进阶

学习完基本的知识之后,可以去看看任玉刚的 Android 开发艺术探索,虽然是几年前出版的,但真的是精华。

接着,可以关注一些架构,性能优化方面的。

  • MVC, MVP, MVVN
  • 内存泄露怎么检测,常见的内存泄露有哪些
  • 怎么检测 ANR,有哪些手段呢,优缺点是什么
  • 怎么捕获 Crash,Java Crash 和 native crash 有哪些不同呢
  • Android 检测启动时间,启动优化怎么做,常见的 Android 启动优化手段有哪些
  • Android gradle,是怎样打包的,怎样提高编译速度

这里推荐几篇不错的文章

Android 启动优化(一) - 有向无环图

Android 启动优化(二) - 拓扑排序的原理以及解题思路

Android 启动优化(三)- AnchorTask 开源了

Android 启动优化(四)- AnchorTask 是怎么实现的

Android 启动优化(五)- AnchorTask 1.0.0 版本正式发布了

Android 启动优化(六)- 深入理解布局优化

关注前沿技术

关注前沿技术,可以去一些论坛,公众号上面逛逛。

论坛的话,推荐掘金,CSDN

Android 技术公众号的话,推荐鸿洋,郭霖的公众号,当然还有我的公众号程序员徐公。文章的质量算是比较高的。

最后,有空的时候多去 github 或者 google 官网 看看,有什么新技术,一般第一时间会更新。

如何进入大厂

面试是一个双向选择的过程,我们要保持足够的自信。

在我看来,要进入大厂,有 4 点最重要。

  1. 编程能力,包括常用技术以及常用技术的原理,毕竟招人,是来干活的,不是来当爹的
  2. 技术深度,在大厂,分工越来越越明确,职责越来越细。很多时候,需要的是某一个技术领域的人才,而不是什么都懂一点的全才。
  3. 算法。
  4. 软技能,沟通能力等

如何学习算法

现如今,如果你想进入大厂,腾讯,阿里,头条,拼多多等,不管是社招还是校招,肯定都会面试到算法的。

相信很多人有这样的想法,面试的时候早火箭,工作的时候拧螺丝。确实,这种情况非常常见,我也认同。但没办法,谁叫我们想进入大厂呢。

不过,这种情况也可以理解。怎么在几轮面试中确定面试者的水平呢?
肯定是考察算法,基础这些,原理这些

虽然这些代表不了全部,但起码能在一定程度上代表了面试者的水平能力。要知道,编程语言其实都是想通的,编程思维和算法能力才是核心。

掌握了原理,编程思维,切换到另外一门语言其实是很快的。这也就是面试官喜欢考察算法和原理的原因。

至于要怎么学习算法,我简单归纳一下

第一:要了解基本的数据结果,数组,链表,Map,Set,二叉树等,了解他们的优缺点,时间复杂度,空间复杂度等

第二:要掌握一些常见的算法,递归,迭代,八大排序,二分查找,贪心算法等

第三:掌握一种算法,不仅要知道 what,还要知道 why(分析各种算法的优缺点),比如 topK问题,有常见的几种解决方案,排序,快排思想,海量数据堆排序

第四:刚开始学的时候,可能会比较吃力,可以先刷题,慢慢找感觉,从易到难

  • 比如,第一天,你刷这道算法题的时候看不懂,先不用着急,很多人都是这样过来的,先搜一下答案,看一下别人是怎么解决的。
  • 看懂了之后,自己用代码写一遍,跑一遍。这很重要,很多时候,你以为你自己懂了,但当你在写的时候是写不出来的,在你动手写代码时,会不断加深你的印象
  • 第二天,自己再写一遍,加深印象

第五:学好算法不是一日之功,需要长期的积累。建议的做法是每天做一两道题,题目不在多,贵在于理解。坚持一两个月,你会发现你的感觉逐渐好起来了。

https://github.com/gdutxiaoxu/Android_interview

Android 学习资料分析

黑马 52 期不加密视频

获取方式在微信公众号 “程序员徐公” 回复“黑马” 两字

01、安卓基础+JNI (14天)
02、Android应用开 发-代码版本管理和实战(1天)
03、android案例与项目_手机安全卫士(12天)
04、自定义控件与视图(2天)
05、android项目 实战_智慧北京(6天)
06、android项 目实战_谷歌应用市场(6天)
07、android进阶 高阶案例-QQ5.0特效专辑(2天)
08、android游戏开发_ 植物大战僵尸(3天)
09、android案例与项目_ 百度地图(1天_ 补充)
10、android项目实战_智能短信管理(3天_ 补充)
11、Android
_WebView&HTML5开发(1天 _补充)

Java 马士兵视频

获取方式在微信公众号 “程序员徐公” 回复“java” 两字

第一部分:J2se学习视频内容包括:

尚学堂科技_马士兵_JAVA视频教程_JDK5.0_下载-安装-配置
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第01章_JAVA简介_源代码_及重要说明
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第02章_递归补充
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第02章_基础语法
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第03章_面向对象
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第04章_异常处理
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第05章_数组
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第06章_常用类
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第07章_容器
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第08章_IO
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第09章_线程
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第10章_网络
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第11章_GUI
尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_专题_日期处理
尚学堂科技_马士兵_JAVA视频教程_J2SE_专题_正则表达式

第二部分:j2se练习项目视频内容包括:

第二部分:j2se练习项目视频内容包括:

尚学堂科技_马士兵_在线聊天系统雏形视频教程_java_eclipse
尚学堂科技_马士兵_坦克大战视频教程_java_eclipse
尚学堂科技_马士兵_坦克大战图片版
尚学堂科技_马士兵_JAVA_坦克大战网络版视频教程
尚学堂科技_马士兵_snake_贪吃蛇内部视频

小结

以上是我这些年来, Android 学习路上的一些简单分享。在 Android 的学习路上,我是站在巨人的肩膀上成长起来的,同样,我也希望成为你们的巨人。

希望我们可以成为朋友,成长路上的忠实伙伴!

Android 黑马学习视频

目录

  • 01、安卓基础+JNI (14天)
  • 02、Android应用开 发-代码版本管理和实战(1天)
  • 03、android案 例与项目_手机安全卫士(12天)
  • 04、自定义控件与视图(2天)
  • 05、android项目实战_ 智慧北京(6天)
  • 06、android项目实战_谷歌应用市场(6天)
  • 07、android进阶高阶案例-QQ5.0特效专辑(2天)
  • 08、android游戏开发_植物大战僵尸(3天)
  • 09、android案例与项目_ .百度地图(1天_ 补充)
  • 10、android项目实战_ 智能短信管理(3天_ 补充)
  • 11、Android_ WebView&HTML5开发(1天 补充)

获取方式

百度云链接

Android 学习 + 面试指南:

如果链接过期了,可以关注我的微信公众号“程序员徐公”,回复“黑马“两字,会自动发给你

  1. 公众号程序员徐公回复黑马,获取 Android 学习视频
  2. 公众号程序员徐公回复徐公666,获取简历模板,教你如何优化简历,走进大厂
  3. 公众号程序员徐公回复面试,可以获得面试常见算法,剑指 offer 题解
  4. 公众号程序员徐公回复马士兵,可以获得马士兵学习视频一份