| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- 小明经过一年的努力学习终于成为Android中级工程师了,月薪变成了17k。到了中级工程师,已经可以在公司里干很多体力活了,但是一些很重要的任务小明还不能一个人承担起来,这个时候小明需要学习的内容就很多了,如下所示:
- - AIDL:熟悉AIDL,理解其工作原理,懂transact和onTransact的区别;
- - Binder:从Java层大概理解Binder的工作原理,懂Parcel对象的使用;
- - 多进程:熟练掌握多进程的运行机制,懂Messenger、Socket等;
- - 事件分发:弹性滑动、滑动冲突等;
- - 玩转View:View的绘制原理、各种自定义View;
- - 动画系列:熟悉View动画和属性动画的不同点,懂属性动画的工作原理;
- - 懂性能优化、熟悉mat等工具
- - 懂点常见的设计模式
- 学习方法
- 阅读进阶书籍,阅读Android源码,阅读官方文档并尝试自己写相关的技术文章,需要有一定技术深度和自我思考。在这个阶段的学习过程中,有2个点是比较困扰大家的,一个是阅读源码,另一个是自定义View以及滑动冲突。
- 如何阅读源码呢?这是个头疼的问题,但是源码必须要读。阅读源码的时候不要深入代码细节不可自拔,要关注代码的流程并尽量挖掘出对应用层开发有用的结论。另外仔细阅读源码中对一个类或者方法的注释,在看不懂源码时,源码中的注释可以帮你更好地了解源码中的工作原理,这个过程虽然艰苦,但是别无他法。
- 如何玩转自定义View呢?我的建议是不要通过学习自定义view而学习自定义view。为什么这么说呢?因为自定义view的种类太多了,各式各样的绚丽的自定义效果,如何学的玩呢!我们要透过现象看本质,更多地去关注自定义view所需的知识点,这里做如下总结:
- - 搞懂view的滑动原理
- - 搞懂如何实现弹性滑动
- - 搞懂view的滑动冲突
- - 搞懂view的measure、layout和draw
- - 然后再学习几个已有的自定义view的例子
- - 最后就可以搞定自定义view了,所谓万变不离其宗
- 大概再需要1-2年时间,即可达到高级工程师的技术水平。我个人认为通过《Android开发艺术探索》和《Android群英传》可以缩短这个过程为0.5-1年。注意,达到高级工程师的技术水平不代表就可以立刻成为高级工程师(受机遇、是否跳槽的影响),但是技术达到了,成为高级工程师只是很简单的事。
- 技术要求:
- - 稍微深入的知识点
- AIDL、Messenger、Binder、多进程、动画、滑动冲突、自定义View、消息队列等
- - 书籍推荐
- 《Android开发艺术探索》、《Android群英传》
- 高级工程师
- 小明成为了梦寐以求的高级工程师,月薪达到了20k,还拿到了一丢丢股票。这个时候小明的Android水平已经不错了,但是小明的目标是资深工程师,小明听说资深工程师月薪可以达到30k+。
- 为了成为Android资深工程师,需要学习的东西就更多了,并且有些并不是那么具体了,如下所示:
- - 继续加深理解”稍微深入的知识点“中所定义的内容
- - 了解系统核心机制:
- 1. 了解SystemServer的启动过程
- 2. 了解主线程的消息循环模型
- 3. 了解AMS和PMS的工作原理
- 4. 能够回答问题”一个应用存在多少个Window?“
- 5. 了解四大组件的大概工作流程
- 6. …
- - 基本知识点的细节
- 1. Activity的启动模式以及异常情况下不同Activity的表现
- 2. Service的onBind和onReBind的关联
- 3. onServiceDisconnected(ComponentName className)和binderDied的区别
- 4. AsyncTask在不同版本上的表现细节
- 5. 线程池的细节和参数配置
- 6. …
- - 熟悉设计模式,有架构意识
- 学习方法
- 这个时候已经没有太具体的学习方法了,无非就是看书、看源码和做项目,平时多种总结,尽量将知识融会贯通从而形成一种体系化的感觉。同时这个阶段对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习。关于设计模式的学习,最近一本新书推荐给大家《Android 源码设计模式解析与实战》,既可以学习设计模式,又可能体会到Android源码中的设计思想,我最近也在阅读此书。
- - 系统核心机制
- - 基本知识点的细节
- - 设计模式和架构
- - 书籍推荐
- 《Android开发艺术探索》、《Android 源码设计模式解析与实战》、《Android内核剖析》
- 没有类的成员变量,可以把该类的方法写成static
- ProgressBar可以在子线程里面更新UI
- 将某一个变量从局部变量移为成员变量快捷键
- 不会被继承所以要在类的前面加上final
- 得到屏幕宽和高:
- DisplayMetrics displayMetrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
- int screenWidth = displayMetrics.widthPixels;
- int screenHeight = displayMetrics.heightPixels;
- 一、下载图片
- 二、解析xml
- 三、冒择入希快归堆
- 四、设计模式
- 五、数据库分页
- 文件流
- 八、多线程
- 页面上现有ProgressBar控件progressBar,请用书写线程以10秒的的时间完成其进度显示工作。
- public class ProgressBarStu extends Activity {
- private ProgressBar progressBar = null;
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.progressbar);
- //从这到下是关键
- progressBar = (ProgressBar)findViewById(R.id.progressBar);
-
- Thread thread = new Thread(new Runnable() {
-
- @Override
- public void run() {
- int progressBarMax = progressBar.getMax();
- try {
- while(progressBarMax!=progressBar.getProgress())
- {
-
- int stepProgress = progressBarMax/10;
- int currentprogress = progressBar.getProgress();
- progressBar.setProgress(currentprogress+stepProgress);
- Thread.sleep(1000);
- }
-
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
- });
-
- thread.start();
- //关键结束
- }
-
- }
- 九、Activity启动模式和Intent有关Activity启动的方式
- 十、数据库
- 1。 创建一个版本为1的“diaryOpenHelper.db”的数据库,
- 2. 同时创建一个 “diary” 表(包含一个_id主键并自增长,topic字符型100长度, content字符型1000长度)
- 3. 在数据库版本变化时请删除diary表,并重新创建出diary表。
- public class DBHelper extends SQLiteOpenHelper {
- public final static String DATABASENAME = "diaryOpenHelper.db";
- public final static int DATABASEVERSION = 1;
-
- String mCreateSQL ="create table diary"+
- "("+
- "_id integer primary key autoincrement,"+
- "topic varchar(100),"+
- "content varchar(1000)"+
- ")";
- db.execSQL(sql);
- //创建数据库
- public DBHelper(Context context,String name,CursorFactory factory,int version)
- {
- super(context, name, factory, version);
- }
- //创建表等机构性文件
- public void onCreate(SQLiteDatabase db)
- {
- db.execSQL(mCreateSQL);
- }
- //若数据库版本有更新,则调用此方法
- public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
- {
-
- String sql = "drop table if exists diary";
- db.execSQL(sql);
- db.execSQL(mCreateSQL);
- }
- }
- 十一、内存泄露情况
- 1.数据库的cursor没有关闭
- 2.构造adapter时没有使用缓存contentview
- 3.Bitmap对象不使用时采用recycle()释放内存
- 4.activity中的对象的生命周期大于activity
- 总结:保存了不可能再被访问的变量类型的引用
- 十三、Message, Handler, Message Queue, Looper之间的关系
- Andriod提供了Handler和Looper来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(Message Exchange).
- 1)Looper:
- 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列).
- 2)Handler:
- 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出所送来的消息.
- android中线程与线程,进程与进程之间如何通信。
- 线程通信使用Handler
- 十四、系统上安装了多种浏览器,能否指定某浏览器访问指定页面?
- 在action赋值为android.intent.action.VIEW“时可接收如下scheme为"http"等等类型的data。所以突发奇想,启动该程序后,指定action及Uri,即访问指定网页
- 十五、SIM卡的文件系统有自己规范,主要是为了和手机通讯,SIM卡本身可以有自己的操作系统,EF就是作存储并和手机通讯用的。
- 十六、判断手机是否有SD卡
- 在程序中访问SDCard,你需要申请访问SDCard的权限。在AndroidManifest.xml中加入访问SDCard的权限如下:
- <!--在SDCard中创建与删除文件权限-->
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <!--往SDCard写入数据权限-->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
- Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED
- 十七、堆和栈
- 十八、传递数据的方式
- 十九、Android UI中的view刷新:多线程和双缓冲
- 二十、算法
- 二十一、排序
- 二十二、Splash
- 二十三、View Page
- 二十四、ViewSwitcher
- 监测网络状况变化
- IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- mNetReceiver = new NetReceiver();
- registerReceiver(mNetReceiver, intentFilter);
- 重启应用
- Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName());
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(i);
- 版本升级只有code变的时候才会出现
- 先static{}再执行构造函数
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 属性动画
- ObjectAnimator.ofFloat(ll_article_detail_bottom, "translationY", toolBarBotom, toolBarBotom + toolBarHeight).setDuration(1000).start();
- 代替webview
- https://github.com/shivasurya/ChromeCustomTabs
- https://github.com/4k3R/chrome-custom-tabs
- https://github.com/WeRockStar/Android-Chrome-Custom-Tabs
- 不要定时去请求,请求成功之后再请求
- 1个字节是8位
- 只有8种基本类型可以算.其他引用类型都是由java虚拟机决定的自己不能操作
- byte 1字节
- short 2字节
- int 4字节
- long 8字节
- float 4字节
- double 8字节
- char 2字节
- boolean 1字节
- volley网络框架
- 为什么使用service:
- http://www.tuicool.com/articles/Yn2YR3
- 安卓进程间通信:
- http://www.jb51.net/article/37797.htm
- Java ->使用 lambda 巩固 性能优化 线程 socket
- 不同颜色柱状图:
- https://bitbucket.org/danielnadeau/holographlibrary/overview
- 复制对象(深浅)
- 弱引用
- 更改线程中的flag,由false变为true的时候重新执行
- 两种办法
- 1.线程终止之后,想重新运行,重新new,然后start
- 2.变量不要放在while(_flag)里,而是
- 销毁时外层设为false,暂停只改变里层的
- while(_Stop)
- {
- if(_pause)
- {
- Thread.Sleep(1000);
- }
- else
- {
- //dosomething
- }
- }
- 先findid,再setListener,因为初始化listener的时候,case里面的会执行
- ndk
- contentprovider
- 自定义控件
- 算法
- 设计模式
- 网络、握手
- jni
- 动画相关
- vpn
- aidl
- widget
- surfaceview 视频播放(多媒体)
- 多线程断点下载
- 底层源码
- openGL
- ImageLoader
- 安卓空格用\u3000
- looper、handler
- webview
- 按照这种方式启动的应用杀不死: system(am start -n 包名)
- kill pid才能杀死
- 这样获取sd卡路径:
- public static final String EXCEL_DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/questionnaire/paper/";
- 不要getAbsolutePath()
- 自己写一个ps命令,覆盖原有命令,不显示自己应用的pid,那么其他应用无法杀掉
- // 复制内容
- ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("password", password);
- clipboard.setPrimaryClip(clip);
- 获取资源Id:
- int resId = getResources().getIdentifier(type, "drawable" , getPackageName());
- aidl
- messenger
- socket
- ndk
- contentprovider
- 通知+顽固service
- widget
- 会动的启动图标
- 换皮肤
- EventBus
- 自定义EmptyView
- View emptyView = LayoutInflater.from(getActivity()).inflate(R.layout.reload_layout, null);
- ((ViewGroup)mListView.getParent()).addView(emptyView);
- mListView.setEmptyView(emptyView);
- /**
- * 采用Pull解析XML内容
- */
- public class PULLPersonService {
-
- /**
- * 使用pull技术生成xml文件
- * @param persons
- * @param writer
- * @throws Throwable
- */
- public static void save(List<Person> persons, Writer writer) throws Throwable{
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(writer);
- serializer.startDocument("UTF-8", true);
-
- serializer.startTag(null, "persons");
- for(Person person : persons){
- serializer.startTag(null, "person");
- serializer.attribute(null, "id", person.getId().toString());
-
- serializer.startTag(null, "name");
- serializer.text(person.getName());
- serializer.endTag(null, "name");
-
- serializer.startTag(null, "age");
- serializer.text(person.getAge().toString());
- serializer.endTag(null, "age");
-
- serializer.endTag(null, "person");
- }
- serializer.endTag(null, "persons");
- serializer.endDocument();
- writer.flush();
- writer.close();
- }
- /**
- * 使用pull技术解析xml
- * @param inStream
- * @return
- * @throws Throwable
- */
- public static List<Person> getPersons(InputStream inStream) throws Throwable{
- List<Person> persons = null;
- Person person = null;
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inStream, "UTF-8");
- int eventType = parser.getEventType();//产生第一个事件
- while(eventType!=XmlPullParser.END_DOCUMENT){//只要不是文档结束事件
- switch (eventType) {
- case XmlPullParser.START_DOCUMENT:
- persons = new ArrayList<Person>();
- break;
-
- case XmlPullParser.START_TAG:
- String name = parser.getName();//获取解析器当前指向的元素的名称
- if("person".equals(name)){
- person = new Person();
- person.setId(new Integer(parser.getAttributeValue(0)));
- }
- if(person!=null){
- if("name".equals(name)){
- person.setName(parser.nextText());//获取解析器当前指向元素的下一个文本节点的值
- }
- if("age".equals(name)){
- person.setAge(new Short(parser.nextText()));
- }
- }
- break;
- case XmlPullParser.END_TAG:
- if("person".equals(parser.getName())){
- persons.add(person);
- person = null;
- }
- break;
- }
- eventType = parser.next();
- }
- return persons;
- }
- }
|