当我们要去看一个陌生的项目的时候,经常会遇到很多类,很烦恼,感觉就像是一片又一片的森林走不到头,然后就会产生烦躁的思想,但是我们都知道,跟着一条主线往下走,我也是这个样子但是还是经常会把自己绕进去。
看代码,或者说学习别人的代码,怎么才算完全懂了?
对于细读代码我觉的下面这些是必要的:
理清楚业务逻辑,不要只是一个方法名一个方法名的向下去追。那么这就要求我们看清楚的这个对象是在那里创建的,这个方法是运行在哪个进程的那个线程里面。关注这个对象是在什么地方new出来的,这个方法的参数是在什么地方创建出来的。一定注意揣摩方法名和变量名的意思,不要图快,多去读注释,理解写代码的人想表达的意思。停下来对思考它这个地方为什么要这样写,如果是你你怎么写,去分析代码的业务是怎样的,都要处理那几种情况,多给自己提出问题,从code中去思考答案,多回头,记住阅读前面的代码遇到的问题,在阅读完后面的代码之后,在从全局的角度去思考这个问题,考虑他为什么这样写。
我认为呢? 首先,牢记我们的目的是什么?理清楚一个方法,变量,功能还是什么的。每一次深入都结合目的思考一下,这样子不容易走丢。接下来我认为的最重要的一点,看接口和抽象类,我认为不管是多复杂的项目,只要我们能,把它的继承关系,理清楚,看到哪里都不会丢,我们知道好的项目他的代码都会做好分层,做好解耦,也就是面向接口编程应用的很好,以前看设计模式,对面向对象6大原则,只是在能读懂的阶段,远没有达到能在代码设计阶段就将他们体现的淋漓尽致,而好的项目源码,他的架构脉络应该是清晰的,也就是抽象类和接口,对于设计模式有很好的应用,我一直认为,抽象的好的话,那么架构就不会太差。
所以再看代码的时候,我认为还应该看清接口的依赖,理清楚回调,一个项目差不多就能想看哪里看哪里了。
下面分享我对android downloadmanager学习,
public long enqueue(Request request) {
ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
}
这个方法很简单,也很重要,封装一个请求,添加到数据库,这里为什么要添加到数据库呢,直接处理不行吗?考虑业务场景,我们在“下载”这个应用里面是不是可以看到自己的下载记录,还可以管理,那这样是不是就有存到数据库的必要了。返回一个id,后面可以通过这个id来,简单的维护一下我们的请求,
那么问题又来了这里只是添加了数据库,系统怎么知道我们要下载东西呢?
熟悉contentprovider的同学都知道,contentprovider可以把数据库的改动通知给关注某个uri的应用。
那么我们如何去找真正的下载的code在哪呢?
我们注意看上买呢insert方法后面跟的那个静态的uri,从这个uri我们就可以找到对应的contentprovider是DownloadProvider,观察这个类我们发现它继承自DocumentsProvider不知道有没有同学用过这个东西呢?提醒以下这个东西在apiguide里面又讲哦!
insertRequestHeaders(db, rowID, values);
notifyContentChanged(uri, match);
// Always start service to handle notifications and/or scanningfinal
Context context = getContext();
context.startService(new Intent(context, DownloadService.class));
ok在downloadProvider的insert方法的最下面我们发现了这样的代码,很明显这里会通知,而且还启动了接受通知的服务,从注释也能肯出来这一点。那么下来我们就来看一下这个服务。
/** * Initializes the service when it is first created */
@Override
public void onCreate() {
super.onCreate();
if (Constants.LOGVV) {
Log.v(Constants.TAG, "Service onCreate");
}
if (mSystemFacade == null) {
mSystemFacade = new RealSystemFacade(this);
}
//用到这个要做定时任务,猜想?
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//推测这是网络下载应该是在这个线程里面进行
mUpdateThread = new HandlerThread(TAG + "-UpdateThread");
mUpdateThread.start();
mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);
//扫描?
mScanner = new DownloadScanner(this);
//通知,这个应该不会错了
mNotifier = new DownloadNotifier(this);
mNotifier.cancelAll();
//观察者,观察数据库的变化,从那个uri也可以看出
mObserver = new DownloadManagerContentObserver();
getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, true, mObserver);
//不知道有没有小伙伴用过这个。
JobScheduler js = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); if (needToScheduleCleanup(js)) { final JobInfo job = new JobInfo.Builder(CLEANUP_JOB_ID, sCleanupServiceName) .setPeriodic(CLEANUP_JOB_PERIOD) .setRequiresCharging(true) .setRequiresDeviceIdle(true) .build(); js.schedule(job);
}}
ok以上就是DownService的oncreate方法了,看完也能猜测一部分代码吧。
以为这是一个服务嘛,所以呢我们继续去看剩余的生命周期方法,在onstartcommand调用了这个方法我们继续看。
public void enqueueUpdate() {
if (mUpdateHandler != null) {
mUpdateHandler.removeMessages(MSG_UPDATE);
mUpdateHandler.obtainMessage(MSG_UPDATE, mLastStartId, -1).sendToTarget();
}}
我们继续去读那个handler,
果然 DownloadService有这麽个观察者,downloaservice这个类我们只看他的下载流程,现在不要在意细节,在oncreate里初始化handler ,而上面观察到数据变化,会发送消息,然后会掉updateLocked()这个方法来查询数据库,拿出请求封装成了一个downloadinfo ,在这类里面有这个方法info.startDownloadIfReady(mExecutor); 见名知意。进去看我们发现他们任务都扔到线程池里面去处理了,当然还有DownloadThread这个类,处理之前是先把请求封装成这个线程的。
ok这就是大概的一个流程,写的有点乱,在这个基础上再来看download的细节就会很轻松了。
网友评论