第一章,

Android 5.6.7新特性


1、RecycleView的自定义分割线

public class DividerItemDecoration extends RecycleView.ItemDecoration {

}

2、自定义点击事件

3、CardView

4、运行时权限问题

Normal Permissions 不涉及用户隐私,在androidMainfest.xml中申明就好

,无需每次使用的时候都检查权限,且用户不可以取消权限;

Dangerous Permission 涉及用户隐私,需要用户授权

checkSelfPermission

onRequestPermissionResult

PermissionsDispatcher


第4章 多线程编程

实现线程的方式

1、thread\runnable

2、再写一遍callable接口的写法

public static class MyCallable implements Callable {

public String call() {

....

}

}

在main方法中

ExecutorService service = Executors.newSingleThreadPool();

Future future = service.submit(new MyCallable()) ;

try {

futurer.get();

}catch() {

}

注意 类变量放在方法区(静态变量,类的共有信息),成员变量放在堆内存上, 局部变量放在栈内存上

3、Volatile 和synchronized关键字的区别

Volatile是可以让多个线程感知这个值的变化,这个值放在主存当中

Synchronized则是锁主这个变量,只有拥有锁的这个线程可以访问,其他线程将被阻塞

Volatile不能保证原子性,不可以用作多线程的自增;

4、重入锁和条件对象

重入锁和不可以重入锁的区别

不可以重入锁是指当前线程在某个方法中已经获取到锁的时候,没有释放前,在其他方法中是没办法再次获取到锁的

重入锁指的是对某个资源可以进行重复加锁

public void count() {

lock.lock();

doadd();

lock.unlock();

}

public void doadd() {

lock.lock();

//dosomething;

lock.unlock();

}

在do add方法中就没办法再次获取到锁

条件对象是指获取到锁后,需要满足的条件,比如支付转账,获取到锁后,因为当前用户余额不满足转账的金额(条件对象),则会阻塞其他线程

所以我们会new Reentantlock().newCondition()l;获取到条件对象,当当前用户余额不满足转账的金额(条件对象)的时候,condition.await();阻塞当前线程。并且放弃锁;当执行完转账操作的时候,则condition.signalAll();唤醒其他线程;

5、阻塞队列

LinkedBlockQueue  先进先出,生产者往队列中放入一个数据后,边返回

6、线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(

//1. 第一个参数是核心的线程数目   , CORE_POOL_SIZE,

//2.第二个参数 是线程池的最大数目, MAX_POOL_SIZE,

//3.第三个参数是非核心线程等待新任务的时长 KEEP_LIVE;

//4.第4个参数是阻塞队列。blockQueue

//5.第5个参数是线程工厂 ThreadFactory

);

线程池的 处理流程:

7、AsyncTask源码分析:

public AsyncTask(@Nullable Looper callbackLooper) {//代表的是否是主线程的loopermHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}};mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected void done() {try {//postResultIfNotInvoked(get());} catch (InterruptedException e) {android.util.Log.w(LOG_TAG, e);} catch (ExecutionException e) {throw new RuntimeException("An error occurred while executing doInBackground()",e.getCause());} catch (CancellationException e) {postResultIfNotInvoked(null);}}};}

上面是构造函数,通常我们在继承AsyncTask的时候,在主线程使用AsyncTask,确实也没必须要在子线程使用啊

new MyAsyncTask(this)

可以看到

WorkerRunnable 实现了callable接口

mFuture实现了futureTask 。mWork作为参数传递给了mFuture,当执行mFuture的run方法的时候会调用到mWorker的call方法(那么什么时候会调用到mFuture的run方法呢??后面会说),然后执行

result = doInBackground(mParams);//此处的doInBackGrong方法是抽象方法由用户实现;

    private Result postResult(Result result) {@SuppressWarnings("unchecked")Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;}

postResult传递结果给InternalHandler,执行完成后,执行finsh方法,否则执行onProgressUpdate方法(用户可以重载)

    private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}}
onPostExecute方法用户可重载
    private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {onPostExecute(result);}mStatus = Status.FINISHED;}

到此,大家看到了熟悉的

doInBackground

onPostExecute

onProgressUpdate

但是

onPreExecute方法呢???AsyncTask的execute方法呢???

  //TODO 必须要在主线程执行,Params就是传入的参数@MainThreadpublic final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}

如下,当用户调用execute方法的时候,会调用executeOnExecutor方法

 @MainThreadpublic final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {if (mStatus != Status.PENDING) {switch (mStatus) {case RUNNING:throw new IllegalStateException("Cannot execute task:"+ " the task is already running.");case FINISHED:throw new IllegalStateException("Cannot execute task:"+ " the task has already been executed "+ "(a task can be executed only once)");}}mStatus = Status.RUNNING;//执行前要做的事情onPreExecute();mWorker.mParams = params;exec.execute(mFuture);return this;}

到这里我们看到了OnPreExecute方法,这里的exec就是传入的sDefaultExecutors;

r.run执行了我们刚才遗留的问题,会调用到mWorker中的call投递结果

//这里的sDefaultExecutors = new SerialExecutor串行的线程池
private static class SerialExecutor implements Executor {final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();Runnable mActive;//这里的r就是mFuturepublic synchronized void execute(final Runnable r) {mTasks.offer(new Runnable() {public void run() {try {//此处执行mFuture run最终会执行mWorker的call方法投递结果r.run();} finally {//执行完了使用线程池调度下一个scheduleNext();}}});   //当前没有任务if (mActive == null) {scheduleNext();}}//如果执行完或者没有任务就会调用线程池执行下一个protected synchronized void scheduleNext() {if ((mActive = mTasks.poll()) != null) {THREAD_POOL_EXECUTOR.execute(mActive);}}}

小小的总结一下AsyncTask的分析吧: 创建了一个串行执行的线程池,当用户在主线程执行new AsyncTask.execute(params)方法的时候,会从当前的线程池中取出一个线程去执行,先可以看到我们的onPreExecute方法,然后线程池中的线程会调用mWorker.call方法,这个方法里封装了用户要复写的ondoingbackground方法,并且最终就结果投递出去,在finsh方法中我们可以看到onPostExecute方法,将状态机置为finish,到此结束

第5章 Retrofit源码分析

5.1 Retrofit 使用举例

1、访问的例子

  /*** 普通GET请求*/private void getIpInformation() {String url = "http://ip.taobao.com/service/";Retrofit retrofit = new Retrofit.Builder().baseUrl(url).addConverterFactory(GsonConverterFactory.create()).build();IpService ipService = retrofit.create(IpService.class);Call<IpModel> call = ipService.getIpMsg();call.enqueue(new Callback<IpModel>() {@Overridepublic void onResponse(Call<IpModel> call, Response<IpModel> response) {Log.e(TAG, "### nResponse: ");String country = response.body().getData().getCountry();Log.i(TAG, "### country" + country);Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();}@Overridepublic void onFailure(Call<IpModel> call, Throwable t) {Log.e(TAG, "### onFailure: ");}});}

特性要注意一个事情,我调试了好久


public interface IpService {@Headers({"Accept-Encoding: application/json","User-Agent: xiaoyubo"})@GET("getIpInfo.php?ip=59.108.54.37")Call<IpModel> getIpMsg();
}

如果使用模拟器进行调测的话请加上上述的请求头!!!!!!!!!

否则请求不到数据,注意当使用其他虚拟机请求的时候,使用fillder抓包看下有没有这个user-Agent的字段,因为有的服务器会拦截这个请求;

再来看下工建造者模式的写法: 以后就不要再写很多的构造函数啦

package com.whut.android_andvaced_light.retrofit;public class NutritionFacts {private final int servingSize; //TODO 必须的private final int servings; //TODO 必须的private final int calories;private final int fat;private final int sodium;private final int carbohydrate;//一个内部类//以后维护的时候,添加新参数的时候也很方便public static class Builder {// Required parametersprivate final int servingSize;private final int servings;// Optional parameters - initialized to default valuesprivate int calories = 0;private int fat = 0;private int carbohydrate = 0;private int sodium = 0;public Builder(int servingSize, int servings) {this.servingSize = servingSize;this.servings = servings;}public Builder calories(int val) {calories = val;return this;}public Builder fat(int val) {fat = val;return this;}public Builder carbohydrate(int val) {carbohydrate = val;return this;}public Builder sodium(int val) {sodium = val;return this;}public NutritionFacts build() {return new NutritionFacts(this);}}//构造函数需要传builder//可以使用单个builder构建多个对象private NutritionFacts(Builder builder) {servingSize = builder.servingSize;servings = builder.servings;calories = builder.calories;fat = builder.fat;sodium = builder.sodium;carbohydrate = builder.carbohydrate;}public static void main(String[] args) {//具有安全性和可读性//build的时候才会验证参数是否正确NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();}
}

第8章 函数响应式编程

异步操作

说道异步操作我们通常是想到 handler + asyncTask的框架,然而随着代码逻辑的复杂程度和请求数量越来越多,不像RXJAVA可以保持很清晰的逻辑,如同流水线一样,RXJAVA一步步将所需要的内容进行加工,最终形成你想要的成品,然后发射给subscribe进行处理。

想想我们的线程切换。通常在先要开启子线程,然后异步请求完成后,通过handler切换到主线程然后再刷新UI

Retrofit retrofit1 = new Retrofit.Builder().baseUrl("/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
final GetRequest_Interface getRequest_interface = retrofit1.create(GetRequest_Interface.class);
getRequest_interface.getCall().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Translation_1>() {@Overridepublic void accept(Translation_1 translation_1) throws Exception {}
});

在MainActivity需要中需要销毁activity的时候就要取消请求

在onStop中

@Overrideprotected void onStop() {LogUtil.d(TAG, "====>onStop");super.onStop();if ((null != mDispose) && (!mDispose.isDisposed())) {mDispose.dispose();}}

RxJava的源码解析

1、RxJava的订阅流程

第一步,首先要创建一个被观察者Observable,有很多方法。just\fromArray\Create

  @Testpublic void TestRxJava() {// 创建一个消费者,也叫观察者,用来消费注册的事件Consumer<String> consumer = o -> {if (!o.isEmpty()) {System.out.println(o);}};// 被观察者注册一个消费的事件Observable.just("1").subscribe(consumer);}
 Consumer<String> successObserver = new Consumer<String>() {@Overridepublic void accept(String o) throws Exception {if (!o.isEmpty()) {System.out.println(o);}}};Consumer<Throwable> failObserver = new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {}};subscribe = Observable.just("1").subscribe(successObserver, failObserver);
 Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {observableEmitter.onNext("1");}}).doOnNext(new Consumer<String>() {@Overridepublic void accept(String s) throws Exception {System.out.println(s + "do on next");}}).subscribe(new Observer<String>() {@Overridepublic void onSubscribe(Disposable disposable) {}@Overridepublic void onNext(String s) {System.out.println(s);}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onComplete() {}});

第二步,创建一个观察者,也就是Consumer

第三步,观察者订阅事件

2、RxJava的线程切换流程

在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)。

Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Action1<Integer>() {@Overridepublic void call(Integer number) {Log.d(tag, "number:" + number);}});

3、RxJava的变换

flatMap & Map

4、RxJava+Retrofit

getUser(userId).doOnNext(new Action1<User>() {@Overridepublic void call(User user) {processUser(user);}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<User>() {@Overridepublic void onNext(User user) {userView.setUser(user);}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable error) {// Error handling...}});

5、RxJava+RxBinding

6、RxJava+RxBus

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

过了个年哈,,继续读书笔记Android进阶解密

第一章 Android 系统架构

1、app 应用层(launcher、dialer、carmera、calendar);

2、framework层(为应用包含系统应用提供api接口 包含view System\Content Providers\Manger(Window Manager、PackageManager、acitivityManager))

3、系统运行层(Native 层 (C++程序库 + AndroidRuntime(coreLibrary)))

4、HAL层,硬件抽象层(wifi bluetooth audio)

5、linux kernel

第二章 Android系统启动

/system/core/init         init 进程是Andoird系统启动的第一个进程

按下电源后,首先系统会加载引导程序bootloader,引导程序启动linux 内核,linixu内核加载完成后,在系统文件中寻找init.rc 文件,启动init进程

init进程又会启动zygote进程;

1、init 进程的入口函数:

init.rc文件会创建init进程,在init进程的main函数中会去解析init.rc的service

这里会启动zygote进程,service是Android init language语句、init进程拉起一个名字为zygote的进程,位于/system/bin/app_process64位置,

1 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
2    class main
3    priority -20
4    user root
5    group root readproc reserved_disk
6    socket zygote stream 660 root system
7    onrestart write /sys/android_power/request_state wake
8    onrestart write /sys/power/state on
9    onrestart restart audioserver
10    onrestart restart cameraserver
11    onrestart restart media
12    onrestart restart netd
13    onrestart restart wificond
14    writepid /dev/cpuset/foreground/tasks

2、init进程是如何启动zygote进程的 app_main.cpp函数中的main函数:

349    if (zygote) {
350        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
351    } else if (className) {
352        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
353    } else {
354        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
355        app_usage();
356        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
357    }

此时启动zygote进程;

3、属性服务

init进程的启动总结:

1、挂在启动所需要的文件目录;

2、初始化和启动属性服务;

3、解析init.rx配置文件并且启动zygote进程;

2、Zygote进程

zygote进程创建了DVM ART 应用进程程序、运行系统的关键服务systemServer;

在init进程中通过AndoridRuntime的start函数 做了如下几个事情;

a、 startVM 开启java虚拟机

b、startJniRegist 注册jni函数

c、找到zygoteinit.java文件的main方法;在1138处执行;由此调用到java层;通过jni调用到了zygoteinit的java层的main函数;

1132    char* slashClassName = toSlashClassName(className != NULL ? className : "");
1133    jclass startClass = env->FindClass(slashClassName);
1134    if (startClass == NULL) {
1135        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1136        /* keep going */
1137    } else {
1138        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1139            "([Ljava/lang/String;)V");
1140        if (startMeth == NULL) {
1141            ALOGE("JavaVM unable to find main() in '%s'\n", className);
1142            /* keep going */
1143        } else {
1144            env->CallStaticVoidMethod(startClass, startMeth, strArray);
1145

zygote进程总结:

创建appRuntime,调用start方法创建zygote进程;

启动jvm 并且注册jni方法;

启动systemServer;

通过jni调用zygoteInit的main函数进入zygote的java层;

等待AMS请求创建新的应用进程;

3、SystemServer进程

用来创建引导服务、核心服务 、其他服务;

如下代码片段;在SystemServer.java文件中

  Installer installer = mSystemServiceManager.startService(Installer.class);
549        traceEnd();
550
551        // In some cases after launching an app we need to access device identifiers,
552        // therefore register the device identifier policy before the activity manager.
553        traceBeginAndSlog("DeviceIdentifiersPolicyService");
554        mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
555        traceEnd();
556
557        // Activity manager runs the show.
558        traceBeginAndSlog("StartActivityManager");
559        mActivityManagerService = mSystemServiceManager.startService(
560                ActivityManagerService.Lifecycle.class).getService();
561        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
562        mActivityManagerService.setInstaller(installer);
563        traceEnd();
564
565        // Power manager needs to be started early because other services need it.
566        // Native daemons may be watching for it to be registered so it must be ready
567        // to handle incoming binder calls immediately (including being able to verify
568        // the permissions for those calls).
569        traceBeginAndSlog("StartPowerManager");
570        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
571        traceEnd();

SystemServer进程总结;

启动binder线程池可以与其他进程通信;

启动系统服务;

4、launcher的启动

由AMS启动;

AMS由SystemServer启动,SystemServer由Zygote进程启动,zygote由Init进程启动,init由init_rc脚本启动;

第17章 内存优化

内存泄露是指本应该在生命周期结束的时候,被GC回收,但是此时它还在被引用,所以致内存泄漏。随着泄漏的累积,app将消耗完内存。

1、常见的内存泄露的情况:

1、在单例中持有Context对象

如下代码片段中,单利模式是他的生命周期和我们的应用是一样长的,当持有Activity的强引用的时候,Acitivy的context不会被回收,造成内存泄露;

public class Singleton {private static Singleton singleton;private Context mContext;private Singleton(Context context) {this.mContext = context;}public Singleton getInstance(Context context) {if (singleton == null) {synchronized (Singleton.class){if (singleton == null) {singleton = new Singleton(context);}}}return singleton;}
}

正确的做法应该是持有ApplicationContext;

再比如如下代码,当子线程持有主线程的引用的时候,子线程未结束前,主线程的activity都不会被回收,但是ondesoty会被执行;

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.e(TAG, "==== onCreate: ====");new Thread(new Runnable() {private  Context context;@Overridepublic void run() {Log.e(TAG, "====run: ===");SystemClock.sleep(5 * 1000);context = MainActivity.this;Log.e(TAG, "run: end");}}).start();}@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG, "======== onDestroy: ========");}

2、handler引起的内存泄露

public class HandlerActivity extends Activity{//可能引起内存泄漏的方法private final Handler mHandler = new Handler(){@Overridepublic void handlerMessage(Message msg){//...}}@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);//延迟5分钟发送消息mHandler.postDelayed(new Runnable(){@Overridepublic void run(){...}},1000*60*5)}
}

非静态的匿名内部类,持有外部类的引用,当Acitivty消失的时候,此时的MessageQueue里的消息还没有被取走,导致handler仍然被引用,且handler是非静态的匿名内部类,持有外部类的引用,故此时Aciticy消失的时候不会被销毁,导致内存泄露;

3、匿名内部类runnable2此时持有了Acitivy的引用,造成runnable2未结束时,activity被销毁但是不会回收;

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_inner_bad);Runnable runnable1 = new MyRunnable();Runnable runnable2 = new Runnable() {@Overridepublic void run() {}};}private static class MyRunnable implements Runnable{@Overridepublic void run() {}}
}

4、集合中的对象要被清理

5、bitmap对象要被回收;

2、内存泄露的相关分析工具

MemtoryMonitor 可以查看内存的使用情况,内存抖动(频繁的分配内存&回收内存,导致绘制线程受阻),是否有频繁的gc.

使用leakCany

1、引入依赖项

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

public class MyApplication extends Application {private static final String TAG = MyApplication.class.getSimpleName();private RefWatcher refWatcher;@Overridepublic void onCreate() {super.onCreate();Log.e(TAG, "===== MyApplication onCreate: =====");refWatcher = setupLeakCanary();}private RefWatcher setupLeakCanary() {if (LeakCanary.isInAnalyzerProcess(this)) {return RefWatcher.DISABLED;}return LeakCanary.install(this);}public static RefWatcher getRefWatch(Context context) {MyApplication myApplication = (MyApplication) context.getApplicationContext();return myApplication.refWatcher;}}

第16 章绘制优化

1、绘制原理

View的绘制包含 measure,layout,draw

60fps(1秒钟60帧)让人感受不到卡顿,当低于60fps之后则明显感觉卡顿,

影响卡顿的原因:

布局layout复杂,无法再10ms内完成绘制;

UI线程做了好事操作

GC回收时间过长或者频繁GC

工具:

Systrace:

不咋好用点击DDMS中的start Method profilng,看到耗时的方法

2、点击hierarchy View查看视图的层级

1、嵌套层级需要合理的减少

2、使用include标签来进行布局的复用

3、用 merge标签来去除多余层级

比入根布局是linearlayout ,linearlayout中有recycleview 每个item有嵌套linearlayout,则 可以使用merge标签,减少嵌套层级

有一个问题就是merge标签最好是用于替代布局方向一致的布局

4、使用viewstub来加高加载速度

viewstub用来替代布局中不需要显示出来的view,在对于viewstub使用inflate房钱前,他是不占布局看下控件和资源的

viewstub,inlate ; viewstub,setvisiabilty(Visiavble) viewStub操作的是布局文件

避免过度绘制就是移除不需要的background

Android进阶之光 读书笔记相关推荐

  1. Android进阶之光读书笔记——第三章:View体系与自定义View

    第三章 View体系与自定义View 本章将介绍Android中十分重要的View,在多本书中View是必讲的一节,Android群英传就讲了不少的View的知识,那么在这里我们再去复习一遍吧 3.1 ...

  2. Android进阶之光读书笔记——第一章:Android新特性

    第一章 Android新特性 主要讲了一些Android 5.0.6.0.7.0新特性 1.1 Android 5.0新特性 Android 5.0 Lollipop是2014年10月发布的,那时候我 ...

  3. Android进阶解密读书笔记(十)——Java虚拟机

    一.Java虚拟机的执行流程 Java虚拟机执行流程分为两大部分:编译时环境和运行时环境,当一个Java文件经过Java编译器编译后会生成class文件,这个class文件会由Java虚拟机来进行处理 ...

  4. 《Android进阶之光》Horizontal 读书笔记

    <Android进阶之光>Horizontal 读书笔记 public class HorizontalView extends ViewGroup {private int lastIn ...

  5. 《android进阶之光》——事件总线(上)

    今日阅读:<android进阶之光>的事件总线部分,整理如下 事件总线知识点: 1.EventBus:就是事件,可以是任意对象 2.Subscriber:事件的订阅者 3.Publishe ...

  6. 进阶三部曲第一部《Android进阶之光》第2版已出版

    本文首发于微信公众号「刘望舒」 其实<Android进阶之光>第二版已经在2021年4月就出版了,但是今天才发现,我去,没在CSDN发文,今天补上~~ 以下是正文 其实不打算出<An ...

  7. 第一行代码Android第二章读书笔记

    第一行代码Android第二章读书笔记 Activity 1.1 手动创建活动 1.2 Toast和Menu/销毁活动 Intent 2.1 显示/隐式 2.2 传递/返回数据 活动的生命周期 3.1 ...

  8. Android进阶三部曲 第一部《Android进阶之光》已出版(内含勘误)

    独立博客版本请点击这里 勘误帖请点击这里 源码地址:https://github.com/henrymorgen/android-advanced-light 进阶三部曲第二部:http://liuw ...

  9. 《Android进阶之光》续作内容简介

    <Android进阶之光>续作的书名现在还没有定,目前已经完稿,预计会在9月出版.本书基于Android8.0,理念和<Android开发艺术探索>相近,是将源码和应用开发相结 ...

最新文章

  1. vb中可视对象的操作
  2. 精准评论,为何广受娱乐类产品的欢迎?
  3. mysql残余文件的清理
  4. Linus Torvalds:回顾Linux20年
  5. [转]tomcat部署与Context
  6. AndroidSDK下载及安装
  7. FineReport.10 一(帆软)(报表基础练习)
  8. aliez歌词_歌曲《aLIEz(TV动画《ALDNOAH.ZERO》ED2)(翻自 mizuki)》完整歌词是什么?谁唱的?...
  9. opencvsharp阈值分割threshold函数的ThresholdTypes
  10. 一人行,必有我师焉 2020.11.22日记
  11. 微信,百度,字节跳动,支付宝小程序注册流程
  12. 关于karabiner的设置
  13. yui2 datatable转换至yui3 (3)
  14. 小巧的绿色html导航引导页
  15. 西门子TIA PORTAL 安装过程中反复要求重新启动计算机问题
  16. 青岛理工大学计算机基础考试,学校召开2019年下半年全国计算机等级考试考务协调会...
  17. DB2 -SQLSTATE
  18. 关于医学和计算机的论文,计算机医学管理论文
  19. 在ios中fixed定位元素丢失,Date兼容NaN
  20. (二)CCD传感器1000帧(可调)采集,fpga主控,sdram存储 68013usb2.0传输到上位机保存显示:fpga逻辑程序

热门文章

  1. Fastapi学习笔记(一)
  2. Ubuntu桌面不见了,桌面找回
  3. 多线程核心8-3:线程三大安全问题之发布与逸出
  4. x轴z轴代表的方向图片_x y z三个轴的方向 x轴、y轴和z轴分别代表的是什么?
  5. 山西大同大学计算机分数线,山西大同大学录取分数线2021是多少分(附历年录取分数线)...
  6. 【网络游戏植入营销案例】
  7. 微信小程序页面添加背景图,图片全屏显示
  8. 迦瓦栈队 团队第一周项目总结
  9. 爱上c++的第六天(核心课程):继承和多态
  10. vscode markdown 导出PDF错误